How to properly define a many-to-many relationship in SQLAlchemy? - python

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")

Related

SQLAlchemy mapping class relationship error

I am facing this InvalidRequestError that my relationships between two classes are not mapped properly. This is one(Question) to Many(Choice) relationship.
from sqlalchemy.ext.declarative import declartive_base
Base = declarative_base()
class ChoiceModel(Base):
__tablename__ = 'choice'
id_seq = Sequence('choice_id_seq', metadata=Base.metadata)
id = Column(Integer, id_seq, server_default=id_seq.next_value(), primary_key=True, index=True)
choice_context = Column(Text, nullable=False)
question_id = Column(Integer, ForeignKey('question.id'))
question = relationship('QuestionModel', back_populates='choices')
class QuestionModel(Base):
__tablename__ = 'question'
id_seq = Sequence('question_id_seq', metadata=Base.metadata)
id = Column(Integer, id_seq, server_default=id_seq.next_value(), primary_key=True, index=True)
correct = Column(Boolean, nullable=False)
choice_list = Column(ARRAY(Integer))
choices = relationship("ChoiceModel", back_populates='question', cascade="all, delete-orphan")
And the error I am getting,
sqlalchemy.exc.InvalidRequestError:
When initializing mapper mapped class ChoiceModel->choice,
expression 'Question' failed to locate a name ('Question').
If this is a class name, consider adding this relationship() to the
<class 'project.core.models.choice_model.ChoiceModel'> class after both dependent classes have been defined.
I have read SQLAlchemy official document similar questions in stackoverflow that most of them used wrong class name, or didn't have back_populates.
My questions are,
should I have to change my classes name, like Choice, Question instead of having postfix Model ??
If I mapped correctly, what causes this error?
I have set my PostgreSQL tables name, choice and question
*python==3.10
*ubuntu==20.04
*fastapi==0.73.0
*SQLAlchemy==1.4.31
*postgresql==14.2
Thanks in advance!

Ordering relationship by property of child elements

I use flask-sqlalchemy on a Flask project to model my database.
I need to sort the elements of a many-to-many relationship based on properties of different child elements of one side.
I have "Work" (the parent element), "Tag" (the children), "Type" (a one-to-many relationship on Tag) and "Block" (a one-to-many relationship on Type). Tags and Works are joined with a mapping table "work_tag_mapping".
In essence, each tag has exactly one type, each type belongs to exactly one block, and many tags can be added on many works.
I now want the list of tags on a work be sorted by block first and type second (both have a "position" column for that purpose).
Here are my tables (simplified for the sake of the question):
class Work(db.Model):
__tablename__ = 'work'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255, collation='utf8_bin'))
tags = db.relationship('Tag', order_by="Tag.type.block.position, Tag.type.position", secondary=work_tag_mapping)
class Tag(db.Model):
__tablename__ = 'tag'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255, collation='utf8_bin'))
type_id = db.Column(db.Integer, db.ForeignKey('type.id'), nullable=False)
type = db.relationship('Type')
work_tag_mapping = db.Table('work_tag_mapping',
db.Column('id', db.Integer, primary_key=True),
db.Column('work_id', db.Integer, db.ForeignKey('work.id'), nullable=False),
db.Column('tag_id', db.Integer, db.ForeignKey('tag.id'), nullable=False)
)
class Type(db.Model):
__tablename__ = 'type'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255, collation='utf8_bin'))
position = db.Column(db.Integer)
block_id = db.Column(db.Integer, db.ForeignKey('block.id'), nullable=False)
block = db.relationship('Block')
class Block(db.Model):
__tablename__ = 'block'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255, collation='utf8_bin'))
position = db.Column(db.Integer)
Now, it is the "order_by" in the "tags" relationship that doesn't work as I initially hoped.
The error I get is "sqlalchemy.exc.InvalidRequestError: Property 'type' is not an instance of ColumnProperty (i.e. does not correspond directly to a Column)."
I am new to SQLalchemy, Flask and indeed Python, and none of the ressources or questions here mention a case like this.
While this appears not to be possible directly, adding a getter and performing the sorting on retrieval does the trick. Adding lazy='dynamic' ensures the collection behaves as a query, so joins can be performed.
_tags = db.relationship('Tag', lazy='dynamic')
#hybrid_property
def tags(self):
return self._tags.join(Type).join(Block).order_by(Block.position, Type.position)

Error when initializing mapper mapped SQLAlchemy

I have a table who has a reference to 2 other tables in a relation One to Many.
I've tried a lot things but i can't find a similar example of what I want to do. Bellow there is code and description when occurs the error.
Conversas.py:
from .model import db
from sqlalchemy.dialects.postgresql import JSON
class Conversa(db.Model):
__tablename__ = "conversas"
id = db.Column('ConversaId',db.Integer, primary_key=True)
fkEmpresa = db.Column('FkEmpresa', db.Integer, db.ForeignKey('empresas.empresaId'),nullable=False)
fkTipoUsuario = db.Column('FKTipoUsuario', db.Integer,db.ForeignKey('tiposUsuarios.tipoUsuarioId'),nullable=False)
# other fields
Empresas.py:
from .model import db
class Empresa(db.Model):
__tablename__ = "empresas"
id = db.Column('empresaId',db.Integer, primary_key=True)
#some fields
empresaCaracteristicas = db.relationship('EmpresaCaracteristica', backref='empresas', lazy=True)
conversas = db.relationship('Conversa', backref='empresas', lazy=True)
TipoUsuario.py:
class TipoUsuario(db.Model):
__tablename__ = "tiposUsuarios"
id = db.Column('tipoUsuarioId',db.Integer, primary_key=True)
#some fields
usuarios = db.relationship('Usuario', backref='tiposUsuarios', lazy=True)
conversas = db.relationship('Conversa', backref='tiposUsuarios', lazy=True)
I've tried to insert some values and this error occurs:
sqlalchemy.exc.InvalidRequestError: When initializing mapper mapped failed to locate a name ("name '' is not defined"). If this is a class name, consider adding this relationship() to the <class 'banco_nestorgr.tabelas.tipoUsuarioModel.TipoUsuario'> class after both dependent classes have been defined
The backref in class TipoUsuario(db.Model): shouldn't be the same
try to rename backref

SQLAlchemy relationship raise Argument error when creating backref

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')

Can't retrieve model with multiple words in flask-sqlalchemy

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.

Categories