How do I retrieve a model that has multiple words in flask-sqlalchemy? The following throws an error:
class Child(Model):
id = Column(Integer, primary_key=True)
user_id = Column(Integer, ForeignKey('user.id'))
parent_one_id = Column(Integer, ForeignKey('parent_one.id'))
class ParentOne(Model):
id = Column(Integer, primary_key=True)
children = relationship('Child', backref='child.id', lazy='dynamic')
Looking at the DB, I see parent_one as the table name for respective model.
Attempt to get parent:
c = Child.query.filter_by(id=1).first()
print c.user.id # Works
print c.parent_one.id # Fails
I get:
AttributeError: 'Child' object has no attribute 'parent_one'
It doesn't have a parent_one attribute because you did nothing to cause one to be created. Add a relationship from Child to ParentOne called parent_one, or do the reverse with a backref named parent_one.
# in Child
parent_one = relationship('ParentOne', backref='children')
# or in ParentOne
children = relationship(Child, backref='parent_one')
Also note that the relationship you did define has an invalid backref name (child.id is not a valid Python variable name). You seem to have confused backref with ForeignKey.
Related
In my Flask application I am using SQLAlchemy, all tables are defined in one single file models.py:
training_ids_association_table = db.Table(
"training_ids_association",
db.Model.metadata,
Column("training_id", Integer, ForeignKey("training_sessions.id")),
Column("ids_id", Integer, ForeignKey("image_data_sets.id")),
)
class ImageDataSet(db.Model):
__tablename__ = "image_data_sets"
id = Column(Integer, primary_key=True)
trainings = relationship("Training", secondary=training_ids_association_table, back_populates="image_data_sets")
class TrainingSession(db.Model):
__tablename__ = "training_sessions"
id = Column(Integer, primary_key=True)
image_data_sets = relationship("DataSet", secondary=training_ids_association_table, back_populates="trainings")
So what I want to achieve here is a many-to-many relationship:
One ImageDataSet can belong to multiple TrainingSession's
One TrainingSession can include multiple ImageDataSet's
However, as soon as I call TrainingSession.query() in my code, the following error is raised:
Exception has occurred: InvalidRequestError
When initializing mapper mapped class ImageDataSet->image_data_sets, expression 'Training' failed to locate a name ('Training'). If this is a class name, consider adding this relationship() to the <class 'app.base.models.ImageDataSet'> class after both dependent classes have been defined.
I found some related threads here, but they are either asking for one-to-many relationships, or they define their tables in different files. Both is not the case here.
Any ideas what I am doing wrong?
You mispelled the names of the models, try this:
training_ids_association_table = db.Table(
"training_ids_association",
db.Model.metadata,
Column("training_id", Integer, ForeignKey("training_sessions.id")),
Column("ids_id", Integer, ForeignKey("image_data_sets.id")),
)
class ImageDataSet(db.Model):
__tablename__ = "image_data_sets"
id = Column(Integer, primary_key=True)
trainings = relationship("TrainingSession", secondary=training_ids_association_table, back_populates="image_data_sets")
class TrainingSession(db.Model):
__tablename__ = "training_sessions"
id = Column(Integer, primary_key=True)
image_data_sets = relationship("ImageDataSet", secondary=training_ids_association_table, back_populates="trainings")
I'm trying to accomplish the following joined inheritance with one-to-one relationship structure with SQLAlchemy: Box and Item are both Elements, and Box has an Item.
The class definitions are as follows:
class Element(Base):
__tablename__ = 'element'
id = Column(Integer, primary_key=True)
el_type = Column(String)
__mapper_args__ = dict(
polymorphic_identity='element',
polymorphic_on=el_type,
)
class Box(Element):
# Joined table inheritance: Element.
__tablename__ = 'box'
id = Column(Integer, ForeignKey('element.id'), primary_key=True)
__mapper_args__ = dict(polymorphic_identity='box')
# One-to-one relationship: Item.
item = relationship('Item',
back_populates='box', uselist=False)
def __init__(self, item_name=None):
self.item = Item(item_name)
class Item(Element):
# Joined table inheritance: Element.
__tablename__ = 'item'
id = Column(Integer, ForeignKey('element.id'), primary_key=True)
__mapper_args__ = dict(polymorphic_identity='item')
# One-to-one relationship: Box.
box_id = Column(Integer, ForeignKey('box.id'))
box = relationship('Box',
back_populates='item')
name = Column(String)
def __init__(self, name):
self.name = name
When I try to instantiate b = Box('rock'), I get:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Box.item - 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.
Now I have gone to SQLAlchemy's page on this issue (great docs, I might add), but I'm not sure how to bridge the gap between their example and my situation. I thought this might fix it:
class Item(Element):
# ...
box = relationship('Box',
back_populates='item', foreign_keys=[id, box_id])
# ...
...but I get the same error.
What am I doing wrong?
UPDATE:
I have now tried using ForeignKeyConstraint constructs in an effort to more explicitly describe the desired behavior to SQLAlchemy; to no avail:
class Box(Element):
# ...
id = Column(Integer, primary_key=True)
# ...
ForeignKeyConstraint([id], ['element.id'])
# ...
class Item(Element):
# ...
id = Column(Integer, primary_key=True)
# ...
# One-to-one relationship: Box.
box_id = Column(Integer)
box = relationship('Box',
back_populates='item')
ForeignKeyConstraint([id, box_id], ['element.id', 'box.id'])
# ...
A simple AssertionError is thrown back at me (no description). Given that SQLAlchemy has a good track record of producing meaningful error messages, could this suggest that this is an unanticipated situation?
I am trying to build a one to many relationship so that each cafe can have many reviews. However, SQLAlchemy raises an ArgumentError when defining the relationship. How do I fix this error?
class Review(db.Model):
id = db.Column(db.Integer, primary_key=True)
cafe = db.Column(db.String, db.ForeignKey('cafe.name'))
class Cafe(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
reviews = db.relationship(Review, backref='cafe')
sqlalchemy.exc.ArgumentError: Error creating backref 'cafe' on relationship 'Cafe.cafes': property of that name exists on mapper 'Mapper|Review|review'
When SQLAlchemy tries to create the backref for the Cafe.cafes relationship called cafe on Review, it finds the column you named cafe and raises an error that it can't use the same name.
Give your foreign keys different names than your relationships/backrefs.
cafe_name = db.Column(db.ForeignKey(Cafe.name))
Alternatively, it can be easier to keep track of names when the foreign key and relationship are defined in the same model.
class Cafe(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
class Review(db.Model):
id = db.Column(db.Integer, primary_key=True)
cafe_id = db.Column(db.ForeignKey(Cafe.id))
cafe = db.relationship(Cafe, backref='reviews')
Here are my models (ignoring imports):
class Parent(Base):
__tablename__ = 'parents'
id = Column(Integer, primary_key=True)
name = Column(String)
children = relationship('Child', backref='parent', lazy='dynamic')
class Child(Base):
__tablename__ = 'children'
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey('parents.id'))
Next I create a parent and a child, and relate them:
dbsession = session()
child = Child(name='bar')
dbsession.add(child)
parent = Parent(name='foo')
parent.children.append(child)
dbsession.add(parent)
dbsession.commit()
And all that works fine (so ignore any errors I may have made copying it to here). Now I'm trying to break the relationship, while keeping both the parent and the child in the database, and I'm coming up empty.
I appreciate any help.
I'm not sure exactly what you mean by break a relationship or why but I think this might work:
child = dbsession.query(Child).filter(Child.name=='bar').one()
child.parent_id = None
dbsession.add(child)
dbsession.commit()
This post gives more info about blanking a foreign key: Can a foreign key be NULL and/or duplicate?
I'm trying to set up a relationship between two tables which allows me to reach obj1.obj2.name where obj1 is one table, and obj2 is another table. Relationship is one-to-one (one person to one geographical region)
# Table one (Person)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
region = db.Column(db.Integer, db.ForeignKey('region.id'))
# Table two (Region)
class Region(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
If I use Person.region (where Person is an object of Person class) I get the int of the primary key of the region of the user, but I would like to get the 'name' field associated with it.
I've figured out that this would work:
region = models.Region.query.filter_by(id=REGION_ID).first().name
but it's not applicable in my case since I need to access the 'name' field from a Flask template.
Any thoughts?
Here I basically use your model, but:
1) changed the name of the FK column
1) added a relationship (please read Relationship Configuration part of the documentation)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
# #note: renamed the column, so that can use the name 'region' for
# relationship
region_id = db.Column(db.Integer, db.ForeignKey('region.id'))
# define relationship
region = db.relationship('Region', backref='people')
class Region(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
With this you are able to get the name of the region as below:
region_name = my_person.region.name # navigate a 'relationship' and get its 'name' attribute
In order to make sure that the region is loaded from the database at the same time as the person is, you can use joinedload option:
p = (db.session.query(Person)
.options(db.eagerload(Person.region))
.get(1)
)
print(p)
# below will not trigger any more SQL, because `p.region` is already loaded
print(p.region.name)