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'))
Related
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.
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)
I'm working with SQLAlchemy and I try to achieve one-to-one and one-to-many relationships on the same parent class.
This is for the simplicity of keeping track of the main child entity.
Unfortunately I'm getting an error:
AmbiguousForeignKeysError: Could not determine join condition between
parent/child tables on relationship Customer.contact - there are
multiple foreign key paths linking the tables. Specify the
'foreign_keys' argument, providing a list of those columns which
should be counted as containing a foreign key reference to the parent
table.
Am I doing something wrong or it is not possible?
Here is a code example:
class Customer(Base):
__tablename__ = 'customer'
id = Column(Integer, primary_key=True)
contact_id = Column(Integer, ForeignKey('contact.id'))
address_id = Column(Integer, ForeignKey('address.id'))
contact = relationship('Contact', backref=backref("contact", uselist=False))
address = relationship('Address', backref=backref("address", uselist=False))
contact_list = relationship('Contact')
address_list = relationship('Address')
class Contact(Base):
__tablename__ = 'contact'
id = Column(Integer, primary_key=True)
customer_id = Column(Integer, ForeignKey(
'customer.id',
use_alter=True, name='fk_contact_customer_id_customer',
onupdate='CASCADE', ondelete='SET NULL'
))
first_name = Column(String(32))
last_name = Column(String(32))
class Address(Base):
__tablename__ = 'address'
id = Column(Integer, primary_key=True)
customer_id = Column(Integer, ForeignKey(
'customer.id',
use_alter=True, name='fk_address_customer_id_customer',
onupdate='CASCADE', ondelete='SET NULL'
))
label = Column(String(32))
Thanks
Apparently the solution was later in the documentation:
SQLAlchemy does not know which foreign key to use, so you have to specify those as Column objects in relationship(foreign_keys=[]) like so:
class Contact(Base):
# ...
customer_id = Column(Integer, ForeignKey(
'customer.id',
use_alter=True, name='fk_contact_customer_id_customer',
onupdate='CASCADE', ondelete='SET NULL'
))
# ...
class Customer(Base):
# ...
contact_id = Column(Integer, ForeignKey('contact.id'))
#...
contact = relationship('Contact', uselist=False, foreign_keys=[contact_id])
contact_list = relationship('Contact', foreign_keys=[Contact.customer_id])
#...
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),
)
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.