Sqlalchemy foreign key to a subset of polymorphic classes - python

I am using sqlalchemy to create a structure that resembles a graph, so that there are several types of nodes and links joining them. The nodes are defined like this:
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
type = Column(Unicode)
__mapper_args__ = {'polymorphic_on': type}
class InputNode(Node):
__tablename__ = 'inputnode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'input'}
class ThruNode(Node):
__tablename__ = 'thrunode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'thru'}
class OutputNode(Node):
__tablename__ = 'outputnode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'output'}
Now I want to create a Link table which would looks something like this:
class Link(Base):
__tablename__ = 'link'
input = Column(Integer, ForeignKey('node.id', where='type IN ("input", "thru")'))
output = Column(Integer, ForeignKey('node.id', where='type IN ("thru", "output")'))
The bit I'm struggling with is how to do the where part of it, since as I've written it is not valid in sqlalchemy. I had though of using a CheckConstraint or a ForeignKeyConstraint, but I can't see how either of them could actually be used to do this.

I haven't tryed it nor am I an expert of this, but shouldn't this work?
class Link(Base):
__tablename__ = 'link'
input = Column(Integer, ForeignKey('thrunode.id'))
output = Column(Integer, ForeignKey('outputnode.id'))
First I had another idea that maybe you could have used different names for the ids and than use those, kind of like:
instead of:
class InputNode(Node):
__tablename__ = 'inputnode'
id = Column(Integer, ForeignKey('node.id'), primary_key=True)
__mapper_args__ = {'polymorphic_identity': 'input'}
this:
class ThruNode(Node):
[...]
thrunode_id = Column(Integer, ForeignKey('node.id'), primary_key=True)
[...]
and then:
class Link(Base):
__tablename__ = 'link'
input = Column(Integer, ForeignKey('node.thrunode_id'))
output = Column(Integer, ForeignKey('node.outputnode_id'))
I got the idea from here sqlalchemy docs: declarative.html#joined-table-inheritance

Related

Selecting specific columns with SQLAlchemy inheritance

Using the example from the SQLAlchemy documentation for inheritance, how would I query the Employee class for only the id and engineer_info columns of all derived classes, including ones which don't have the engineer_info column?
class Employee(Base):
__tablename__ = 'employee'
id = Column(Integer, primary_key=True)
name = Column(String(50))
type = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'employee',
'polymorphic_on':type,
}
class Engineer(Employee):
__tablename__ = 'engineer'
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
engineer_info = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'engineer',
}
class Manager(Employee):
__tablename__ = 'manager'
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
manager_data = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'manager',
}
It looks like you can do this if you explicitly join the employee table to engineer table in a left/outer join, like:
session.query(Employee.id, Engineer.engineer_info).join(Engineer, isouter=True).all()
or:
session.query(Employee.id, Engineer.engineer_info).outerjoin(Engineer).all()
For future searchability: See also SQLAlchemy inheritance filter on all columns which I found by googling "sqlalchemy left join subclass"

How to determine one to one relationship? [duplicate]

I have two tables, foo and bar, and I want foo.bar_id to link to bar. The catch is that this is a one-way one-to-one relationship. bar must not know anything about foo. For every foo, there will be one and only one bar.
Ideally, after selecting a foo, I could do something like this:
myfoo.bar.whatever = 5
How to accomplish this?
The documentation explains this nicely:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child = relationship("Child", uselist=False, backref="parent")
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
OR
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'))
child = relationship("Child", backref=backref("parent", uselist=False))
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
If you want a true one-to-one relationship, you also have to use the "uselist=False" in your relationship definition.
bar_id = Column(Integer, ForeignKey(Bar.id))
bar = relationship(Bar, uselist=False)
I think if it is a truly one to one relationship we should add a uniqueness constraint to foreign key so another parent can not have other parent child!! Like this:
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.id'), unique=True)
child = relationship("Child", backref=backref("parent", uselist=False))
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
It turns out this is actually quite easy. In your Foo model:
bar_id = Column(Integer, ForeignKey(Bar.id))
bar = relationship(Bar)

In sqlalchemy how to access anonymous field in subquery

First, I used Sqlalchemy's polymorphic architecture.
ChildA and ChildB extends Child.
ChildA has name column.
ChildB has age column.
class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship(Parent, backref='children')
class ChildA(Child):
__tablename__ = 'child_a'
id = Column(Integer, primary_key=True)
name = Column(String(50))
class ChildB(Child):
__tablename__ = 'child_b'
id = Column(Integer, primary_key=True)
age = Column(Integer)
parent = DBSession.query(Parent).first()
subquery = parent.children.join(ChildA).subquery()
So I want to access ChildA.name column from subquery.
Something like subquery.c.ChildA.name == 'Tom'
If I understood properly what you are trying to do, you don't really need a subquery, it could be simply something like
In [13]:
f = session.query(Parent, ChildA).join(ChildA).first()
print(f.ChildA.name)
Pedro
For the use of subqueries, I would recommend you take a look to sqlalchemy tutorial.
On the other hand, I wasn't able to use the classes as you've defined them, I had to add a ForeignKey like this
class ChildA(Child):
__tablename__ = 'child_a'
id = Column(Integer, ForeignKey('child.id'), primary_key=True)
name = Column(String(50))
class ChildB(Child):
__tablename__ = 'child_b'
id = Column(Integer, ForeignKey('child.id'), primary_key=True)
age = Column(Integer)
I have no doubt that it works for you, this probably depends on the setup.
And finally, I would like to recommend you to use a column for the type of child. With this, it will be easier to recognize the children you are using. Something like this,
class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))
parent = relationship(Parent, backref='children')
type = Column(String(20))
__mapper_args__ = {
'polymorphic_identity':'child',
'polymorphic_on':type
}
class ChildA(Child):
__tablename__ = 'child_a'
id = Column(Integer, ForeignKey('child.id'), primary_key=True)
name = Column(String(50))
__mapper_args__ = {
'polymorphic_identity':'child_a',
}
class ChildB(Child):
__tablename__ = 'child_b'
id = Column(Integer, ForeignKey('child.id'), primary_key=True)
age = Column(Integer)
__mapper_args__ = {
'polymorphic_identity':'child_b',
}
Please take a look to sqlalchemy docs for details.
Hope it helps.

Deleting from self-referential inherited objects does not cascade in SQLAlchemy-SQLite

I posted a question previously here where I tried to build a hierarchy using different objects. Each object can have any type of object as it's parent, and any type as children. I solved it by using the Node class suggested by SQLAlchemy here and letting the other objects inherit from it.
Now I'm having the problem that deleting a node does not delete it's children. I have tried a lot of solutions like different cascade settings, using ondelete='CASCADE' in the foreignkey, as well as DBSession.execute('pragma foreign_keys=on') but none are working. I think the problem is in the ParentID key because in the child it is not null when the parent is delete.
I'm pretty new to SQLAlchemy so I'm not at all sure where I'm going wrong, any help would be appreciated.
These are my current models:
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
# DBSession.execute('pragma foreign_keys=on')
Base = declarative_base()
class Node(Base):
def getID():
return uuid.uuid1().hex
__tablename__ = 'Node'
ID = Column(Text, primary_key=True, default=getID)
ParentID = Column(Text, ForeignKey('Node.ID', ondelete='CASCADE'))
type = Column(Text(50))
Children = relationship("Node",
backref=backref('Parent',
remote_side=[ID]
),
single_parent=True,
cascade="all, delete, delete-orphan",
passive_deletes = True
)
__mapper_args__ = {
'polymorphic_identity':'Node',
'polymorphic_on':type
}
class A(Node):
__tablename__ = 'A'
ID = Column(Text, ForeignKey('Node.ID', ondelete='CASCADE'), primary_key=True)
Name = Column(Text)
Description = Column(Text)
__mapper_args__ = {'polymorphic_identity':'A'}
class B(Node):
__tablename__ = 'B'
ID = Column(Text, ForeignKey('Node.ID', ondelete='CASCADE'), primary_key=True)
Name = Column(Text)
Description = Column(Text)
__mapper_args__ = {'polymorphic_identity':'B'}
class C(Node):
__tablename__ = 'C'
ID = Column(Text, ForeignKey('Node.ID', ondelete='CASCADE'), primary_key=True)
Name = Column(Text)
Description = Column(Text)
Quantity = Column(Integer)
Rate = Column(Integer)
__mapper_args__ = {'polymorphic_identity':'C' }
This is how I build a hierarchy:
a = A(Name="PName",
Description="PDesc",
ParentID='0')
b = B(Name="BGName",
Description="BGDesc",
ParentID=project.ID)
c = C(Name="BIName",
Description="BIDesc",
Quantity=10,
Rate=5,
ParentID=budgetgroup.ID)
# Append the children nodes to their parents
b.Children.append(c)
a.Children.append(b)
DBSession.add(a)
And this is how I delete it:
def deleteitem(id):
deleteid = id
deletethis = DBSession.query(Node).filter_by(ID=deleteid).first()
qry = DBSession.delete(deletethis)
# qry = DBSession.query(Node).filter_by(ID=deleteid).delete(
# synchronize_session='fetch')
transaction.commit()
Note: neither the one way or the other commented out delete cascades.
I was able to find a solution from this answer here.
Now my Node class looks as follows:
class Node(Base):
__tablename__ = 'Node'
ID = Column(Integer, primary_key=True)
ParentID = Column(Integer, ForeignKey('Node.ID', ondelete='CASCADE'))
type = Column(Text(50))
Children = relationship(
'Node',
cascade="all",
backref=backref("Parent", remote_side='Node.ID'),
)
__mapper_args__ = {
'polymorphic_identity':'Node',
'polymorphic_on':type
}
And this seems to work, all my deletes are cascading.

SQLAlchemy - Mapping self-referential relationship as one to many (declarative form)

I want to map a Tag entity using declarative method with SQLAlchemy. A tag can have a parent (another Tag).
I have:
class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, primary_key=True)
label = Column(String)
def __init__(self, label, parentTag=None):
self.label = label
How can I add the "parent" relationship?
You add a ForeignKey referencing the parent, and then create a relationship that specifies the direction via remote_side. This is documented under adjacency list relationships. For declarative you'd do something like this:
class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, primary_key=True)
label = Column(String)
parent_id = Column(Integer, ForeignKey('tag.id'))
parent = relationship('Tag', remote_side=[id])
If you want the reverse relation also, add backref='children' to the relationship definition.
If you need children, you need to use uselist:
class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, primary_key=True)
label = Column(String)
child_id = Column(Integer, ForeignKey('tag.id'))
children = relation('Tag', remote_side=[id], uselist=True)
class Company(BaseModel):
__tablename__ = 'companies'
companyName = db.Column(db.String(50))
contactPerson = db.Column(db.String(50))
email = db.Column(db.String(50))
mobile = db.Column(db.String(20))
parentID = db.Column(db.Integer, db.ForeignKey('companies.id')) # parent company ID
childrenCompany = db.relationship('Company', remote_side='Company.id',
backref=db.backref('children_company')) # parent Company
use:
In [2]: company_query = Company.query.get_or_404(1)
In [3]: company_query.children_company
Out[3]:
[<app.models.user.Company at 0x10f527850>,
<app.models.user.Company at 0x10f527c10>]
parent = relation('Tag') — see http://www.sqlalchemy.org/docs/05/reference/ext/declarative.html#configuring-relations.

Categories

Resources