I am working with the following database table design. The relationship is one-to-many, parent to child. So one parent (mother or father) has multiple children. I'm having issues mapping these tables because the child's column parent_id is a Foreign Key to both of the parent tables.
class Mother(Base):
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.unique_id'))
class Father(Base):
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.unique_id'))
Class Children(Base):
id = Column(Integer, primary_key=True)
unique_id = Column(Integer, ForeignKey('mother.child_id')
I have tried defining a relationship with backref in the parent tables to avoid explicitely defining Foreign Keys in the child tables:
class Mother(Base):
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.unique_id'))
children = relationship('Child', backref='mother')
class Father(Base):
id = Column(Integer, primary_key=True)
child_id = Column(Integer, ForeignKey('child.unique_id'))
children = relationship('Child', backref='father')
Class Children(Base):
id = Column(Integer, primary_key=True)
unique_id = Column(Integer)
But this yields a one-to-one relationship.
I think that foreign keys should bo on Child table, it would be better and more realistic model:
class Mother(Base):
id = Column(Integer, primary_key=True)
children = relationship('Child', backref='mother')
class Father(Base):
id = Column(Integer, primary_key=True)
children = relationship('Child', backref='father')
class Child(Base):
id = Column(Integer, primary_key=True)
father_id = Column(Integer, ForeignKey('father.id'))
mother_id = Column(Integer, ForeignKey('mother.id'))
In above model, each child has at most one mother and one father, while a mother/father can have many children.
Related
Using SQLAlchemy I have three models: Parent1, Parent2, and Child, where Parent1 has one-to-one relationship with Parent2, and both of them has the same relationship with Child. Here are they:
from extensions import db_session
class Parent1(Base):
__tablename__ = 'parent1'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent2 = relationship("Parent2", backref="parent1", uselist=False)
child = relationship("Child", backref="parent1", uselist=False)
class Parent2(Base):
__tablename__ = 'parent2'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent1_id = Column(Integer, ForeignKey('parent1.id'))
child = relationship("Child", backref="parent2", uselist=False)
class Child(Base):
id = Column(Integer, primary_key=True)
parent1_id = Column(Integer, ForeignKey('parent1.id'))
parent2_id = Column(Integer, ForeignKey('parent2.id'))
What I am trying to achieve is to fill Child table with its parents' foreign keys.
So, when I execute this:
parent1 = Parent1(name="Adil")
parent1.parent2 = Parent2(name="Aisha")
parent1.child = Child()
db_session.add(parent1)
db_session.commit()
to the parents tables it inserts data as needed, however to the Child table it inserts data like this:
Child
id parent1_id parent2_id
1 1 NULL
How to properly set relationships, so that on any insert to the Parent1->Parent2 tables it also inserts its ids as foreign keys to Child table?
What I want to achieve is:
Child
id parent1_id parent2_id
1 1 1
I came up with this solution. I removed declared model Child and created association table between two models and added secondary parameter filling with assocation table name:
Base = declarative_base()
child = Table('child', Base.metadata,
Column('parent1_id', Integer, ForeignKey('parent1.id')),
Column('parent2_id', Integer, ForeignKey('parent2.id')))
class Parent1(Base):
__tablename__ = 'parent1'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent2 = relationship("Parent2", secondary='child', uselist=False)
class Parent2(Base):
__tablename__ = 'parent2'
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
parent1_id = Column(Integer, ForeignKey('parent1.id'))
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)
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.
I have two simple models:
class Message(Backend.instance().get_base()):
__tablename__ = 'messages'
id = Column(Integer, primary_key=True, autoincrement=True)
sender_id = Column(Integer, ForeignKey('users.id'))
content = Column(String, nullable=False)
class ChatMessage(Message):
__tablename__ = 'chat_messages'
id = Column(Integer, ForeignKey('messages.id'), primary_key=True)
receiver_id = Column(Integer, ForeignKey('users.id'))
How to define constraint sender_id!=receiver_id?
This doesn't seem to work with joined table inheritance, I've tried and it complains that the column sender_id from Message doesn't exist when creating the constraint in ChatMessage.
This complaint makes sense, since sender_id wouldn't be in the same table as receiver_id when the tables are created, so the foreign key relationship would need to be followed to check the constraint.
One option is to make ChatMessage a single table.
Use CheckConstraint, placed in table args.
class ChatMessage(Base):
__tablename__ = 'chat_messages'
id = sa.Column(sa.Integer, primary_key=True)
sender_id = sa.Column(sa.Integer, sa.ForeignKey(User.id))
receiver_id = sa.Column(sa.Integer, sa.ForeignKey(User.id))
content = sa.Column(sa.String, nullable=False)
__table_args__ = (
sa.CheckConstraint(receiver_id != sender_id),
)
There is an sqlalchemy object
class Employee(Base):
__tablename__ = "employee"
# Columns
id = Column(Integer, primary_key=True, autoincrement=True)
representative_id = Column(Integer, ForeignKey('employee.id'))
parent_id = Column(Integer, ForeignKey('employee.id'))
affiliate_id = Column(Integer, ForeignKey('employee.id'))
# Relatonships
representative = relationship("Employee", ????)
parent = relationship("Employee", ????)
affiliate = relationship("Employee", ????)
How to configure the relationships in the right way if an employee can have, 0 or 1 parent, affiliate and representative? Do know these three are different ones. DB - MySQL
I found out how do to it by myself:
class Employee(Base):
__tablename__ = "employee"
# Columns
id = Column(Integer, primary_key=True, autoincrement=True)
representative_id = Column(Integer, ForeignKey('employee.id'))
parent_id = Column(Integer, ForeignKey('employee.id'))
affiliate_id = Column(Integer, ForeignKey('employee.id'))
# Relatonships
representative = relationship("Employee",
primaryjoin="Employee.representative_id==Employee.id",
remote_side=[id])
parent = relationship("Employee",
primaryjoin="Employee.parent_id==Employee.id",
remote_side=[id])
affiliate = relationship("Employee",
primaryjoin="Employee.affiliate_id==Employee.id",
remote_side=[id])