I have two models with a relationship between them something like this:
Base = declarative_base()
class Model1(Base):
__tablename__ = 'm1'
id = Column(Integer, primary_key=True, index=True, unique=True)
Name = Column(String(255))
info = Column(String(255))
m2s = relationship("Model2", back_populates="m1")
class Model2(Base):
__tablename__ = 'm2'
id = Column(Integer, primary_key=True, index=True, unique=True)
Name = Column(String(255))
info = Column(String(255))
m1 = Column(Integer, ForeignKey('m1.id'))
Now the issue arises when I try to insert a record into Model2 which doesn't have a corresponding Foreign Key in Model1. The error I get is something like this:
(pymysql.err.IntegrityError) (1452, 'Cannot add or update a child row: a foreign key constraint fails'
Tried looking for many solutions, but none helped. I'm sure there must be a way to insert data in Model2 without having to bother about its Foreign Key constraint.
Related
I am into a very confusing situation where I have one to many relation and I want to query data like I want all parent table data but want to get only data from child tables which fulfill condition of site_id = 100.
class Policy(Base):
"""table containing details for Policies"""
__tablename__ = "UmbrellaPolicy"
id = Column(Integer, primary_key=True)
policy_id = Column(Integer, nullable=False, index=True)
user_defined_name = Column(String(255), nullable=True)
and child is like this
class Site(Base):
__tablename__ = "Site"
id = Column(Integer, primary_key=True)
policy_id = Column(Integer, ForeignKey("Policy.id"))
site_id = Column(String(32), nullable=False, index=True)
policy = relationship("Policy", backref="sites")
You should be able to filter join relations like this
parents = Policy.objects.filter(site__site_id=100)
You can find more info about the Django query API here but its generally of the form where you reference the relation with classname__columnname there are many other ways to filter/query that you can reference in the docs
Let's assume we have the following code in some Models.py file:
class Person(db.Model):
__tablename__ = 'Persons'
ID = db.Column(db.Integer, primary_key=True, nullable=False)
Name = db.Column(db.String(255), nullable=False)
class House(db.Model):
__tablename__ = 'Houses'
ID = db.Column(db.Integer,primary_key=True,nullable=False)
OwnerID = db.Column(db.Integer, nullable=False)
TenantID = db.Column(db.Integer, nullable=False)
__table_args__ = (
db.ForeignKeyConstraint(
['OwnerID'],
['Persons.ID'],
),
db.ForeignKeyConstraint(
['TenantID'],
['Persons.ID'],
),
)
OwnerBackref = db.relationship('Person', backref='OwnerBackref', lazy=True, foreign_keys=[OwnerID])
TenantBackref = db.relationship('Person', backref='TenantBackref', lazy=True, foreign_keys=[TenantID])
And we want to reflect these models using the automap base, so we have this code in another module called Database.py:
Base = automap_base()
engine = create_engine(DB_CONNECTION, pool_size=10, max_overflow=20)
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
Base.prepare(engine, reflect=True)
Person = Base.classes.Persons
House = Base.classes.Houses
Now, when I import House in some other module I want to be able to do this:
h = db_session.query(House).first()
print(h.OwnerBackref.Name)
print(h.TenantBackref.Name)
But instead I get an error saying that those 2 backrefs do not exist and instead a field called 'persons' gets added to my House object but the problem here is that it links only 1 (either the Tenant either the Owner). By this I mean that if I do this:
print(h.persons.Name)
It will only print the Name either for the respective Tenant either for the Owner leaving me with no way of accessing the informations for the other one. (Note here that the names that I set to the backrefs are nowhere to be found)
So, my question is how can I use the backrefs I created to access my desired informations ? Am I doing something wrong here ?
The error in your code is that you are using foreign_keys= to define the relationship between the tables but you are passing the local key name not the foreign key name to the function. For your code you cannot use foreign_keys= to define the relationship within the House model as there is only one possible foreign key Person.ID but two possible local keys House.OwnerID and House.TenantID. The primaryjoin= argument should be used instead to specify this.
class Person(db.Model):
__tablename__ = 'Persons'
ID = db.Column(db.Integer, primary_key=True)
Name = db.Column(db.String(255), nullable=False)
class House(db.Model):
__tablename__ = 'Houses'
ID = db.Column(db.Integer,primary_key=True)
OwnerID = db.Column(db.Integer, db.ForeignKey('Persons.ID'), nullable=False)
TenantID = db.Column(db.Integer, db.ForeignKey('Persons.ID'), nullable=False)
Owner = db.relationship('Person', backref='HousesOwned', primaryjoin='House.OwnerID == Person.ID')
Tenant = db.relationship('Person', backref='HousesOccupied', primaryjoin='House.TenantID == Person.ID')
If you placed the relationship statements in in the Person model rather than the House model then you could use either foreign_keys= or primaryjoin= to define the relationship. The following code will result in exactly the same relationships as in the previous code.
class Person(db.Model):
__tablename__ = 'Persons'
ID = db.Column(db.Integer, primary_key=True)
Name = db.Column(db.String(255), nullable=False)
HousesOwned = db.relationship('House', backref='Owner', foreign_keys='[House.OwnerID]')
HousesOccupied = db.relationship('House', backref='Tenant', foreign_keys='[House.TenantID]')
class House(db.Model):
__tablename__ = 'Houses'
ID = db.Column(db.Integer,primary_key=True)
OwnerID = db.Column(db.Integer, db.ForeignKey('Persons.ID'), nullable=False)
TenantID = db.Column(db.Integer, db.ForeignKey('Persons.ID'), nullable=False)
I have two models, related with many-to-many, one of them is hierarchical model:
#hierarchical model
class Tag(Base):
__tablename__ = "tags"
id = Column(Integer, primary_key=True)
name = Column(String)
Tag.parent_id = Column(Integer, ForeignKey(Tag.id, ondelete='CASCADE'))
Tag.childs = relationship(Tag, backref=backref('parent', remote_side=[Tag.id]),
cascade="all, delete")
class Subject(Base):
__tablename__ = "subjects"
id = Column(Integer, primary_key=True, doc="ID")
name = Column(String)
tags = relationship(Tag, secondary="tags_subjects", backref="subjects")
#many-to-many relations model
class TagsSubjects(Base):
__tablename__ = "tags_subjects"
id = Column(Integer, primary_key=True)
tag_id = Column(Integer, ForeignKey("tags.id"))
subject_id = Column(Integer, ForeignKey("subjects.id"))
So, I'll try to explain what I want to do... I want to make one (or several) query, for search all Subject's objects,
that have 'name' field value like 'foo' OR that has related tags having names with values like 'foo'
OR that has related tags, that has one or more parents (or above by hierarchy) tag with 'name' value like 'foo'
I've tried to do somethis like this:
>>> subjects = session.query(Subject).filter(or_(
Subject.name.ilike('%{0}%'.format('foo')),
Subject.tags.any(
Tag.name.ilike('%{0}%'.format('foo')))
)).order_by(Subject.name).all()
But it isn't correct and "flat" query, without hierarchical feature :(
How to do this by SQLAlchemy's API?
Thanks!
P.S. I'm using SQLite backend
I have two models:
class Report(Base):
__tablename__ = 'report'
id = Column(Integer, primary_key=True)
class ReportPhoto(Base):
__tablename__ = 'report_photo'
id = Column(Integer, primary_key=True)
report_id = Column(Integer, ForeignKey(Report.id), nullable=False)
report = relationship(Report, uselist=False, backref=backref('report_photo', uselist=True))
And I would like to add column to Report model which indicates is there any records within ReportPhoto. I try to use column_property this way:
class Report(Base):
__tablename__ = 'report'
id = Column(Integer, primary_key=True)
has_photo = column_property(
select(ReportPhoto.any())
)
but get an error NameError: name 'ReportPhoto' is not defined. How I can fix this issue?
something like that should work:
class ReportPhoto(Base):
__tablename__ = 'report_photo'
id = Column(Integer, primary_key=True)
report_id = Column(Integer, ForeignKey('report.id'), nullable=False)
class Report(Base):
__tablename__ = 'report'
id = Column(Integer, primary_key=True)
report_photos = relationship(ReportPhoto, backref='report')
has_photo = column_property(
exists().where(ReportPhoto.report_id==id)
)
I will add to #Vladimir lliev's response with some clarification for anyone else that might not see how to do this.
Place the table that will have the 'foreign table referencing' column_property after that which it references. In this case, it means placing Report after ReportPhoto. This will solve your NameError, however, you would be left with a new error on your ReportPhoto foreign key reference. To solve this, place your foreign key table reference in quotes. You can read more by referencing the declarative documentation (e.g., declarative.py) and looking under "Configuring Relationships" --- specifically, read the portion on quoting your foreign references.
With your code, this would look like:
class ReportPhoto(Base):
# This now goes first
__tablename__ = 'report_photo'
id = Column(Integer, primary_key=True)
# Notice the quotations around Report references here
report_id = Column(Integer, ForeignKey("Report.id"), nullable=False)
# Notice the quotations around Report references here
report = relationship("Report",
uselist=False,
backref=backref("report_photo", uselist=True))
class Report(Base):
# This is now _after_ ReportPhoto
__tablename__ = 'report'
id = Column(Integer, primary_key=True)
# ReportPhoto now exists and we will not trip a NameError exception
has_photo = column_property(
select(ReportPhoto.any())
)
I created a many to many relationship with sqlalchemy like this:
subject_books = Table('subject_books', Base.metadata,
Column('subject_id', Integer, ForeignKey('subjects.id')),
Column('book_id', Integer, ForeignKey('books.id')),
Column('group', Integer)
)
class Subject(Base):
__tablename__ = 'subjects'
id = Column(Integer, primary_key=True)
value = Column(Unicode(255), unique=True)
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(Unicode(255))
isbn = Column(Unicode(24))
subjects = relationship('Subject', secondary=subject_books, collection_class=attribute_mapped_collection('group'), backref='books')
after that I created a test like following:
book = Book(title='first book',isbn='test')
book.subjects[0] = Subject(value='first subject')
book.subjects[1] = Subject(value='second subject')
session.add(book)
transaction.commit()
and it works fine. But what I really want is to store more than one subject with the same group value, so I tried the following test:
book = Book(title='first book',isbn='test')
book.subjects[0] = [Subject(value='first subject'),Subject(value='second subject')]
book.subjects[1] = [Subject(value='third subject'),Subject(value='forth subject')]
session.add(book)
transaction.commit()
but it does not work.
Can this be done using sqlalchemy?
Thanks in Advance
Razi
I think you are constructing wrong relation ship.
Your relation ship must be
book M2M subject
subject M2M group
So you have to create one more model for group and that must be assign as m2m in Subject
Your models will be like.
subject_books = Table('subject_books', Base.metadata,
Column('subject_id', Integer, ForeignKey('subjects.id')),
Column('book_id', Integer, ForeignKey('books.id')),
)
subject_group = Table('subject_groups', Base.metadata,
Column('group_id', Integer, ForeignKey('groups.id')),
Column('subject_id', Integer, ForeignKey('subjects.id')),
)
class Subject(Base):
__tablename__ = 'subjects'
id = Column(Integer, primary_key=True)
value = Column(Unicode(255), unique=True)
groups = relationship('Groups', secondary=subject_groups, backref='subjects')
class Groups(Base):
__tablename__ = 'groups'
id = Column(Integer, primary_key=True)
name = Column(Unicode(255), unique=True)
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(Unicode(255))
isbn = Column(Unicode(24))
subjects = relationship('Subject', secondary=subject_books, backref='books')
I also check the docs for attribute_mapped_collection. But each time I found that each key is associated with only one object not more then one. If you read anywhere then please provide the link so I can check that how it will be fit in your code.
I think this will be help you.