Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy back_populates many-to-many with multiple associations (tag table)

My question: Since Tag is connected to multiple tables via association tables, what would I put for back_populates on Tag. Is back_populates also necessary on the association tables? I'm not sure how to model back_populates in this scenario. See below for further details.

I have the following table structure:

enter image description here

Essentially, I can apply a tag to a source, or to a source key. Here are my models:

class Tag(Base):
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)

source_tag_assoc = Table('source_tag_assoc', Base.metadata,
    Column('source_id', Integer, ForeignKey('source.id')),
    Column('tag_id', Integer, ForeignKey('tag.id'))
)
class Source(Base):
    id = Column(Integer, primary_key=True, index=True)
    tags = relationship("Tag", secondary=source_tag_assoc, back_populates="source")

source_key_tag_assoc = Table('source_tag_assoc', Base.metadata,
    Column('source_id', Integer, ForeignKey('source_key.id')),
    Column('tag_id', Integer, ForeignKey('tag.id'))
)
class SourceKey(Base):
    id = Column(Integer, primary_key=True, index=True)
    tags = relationship("Tag", secondary=source_key_tag_assoc, back_populates="source_key")
like image 357
Franz Kafka Avatar asked Feb 01 '26 21:02

Franz Kafka


1 Answers

back_populates must point to an actual relationship. If you don't want to define the relationship on both models, then backref should be used instead.

Here's how you'd adapt the models to work with back_populates:

class Tag(Base):
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    source = relationship('Source', secondary=source_tag_assoc, back_populates='tags')

class Source(Base):
    id = Column(Integer, primary_key=True, index=True)
    tags = relationship("Tag", secondary=source_tag_assoc, back_populates="source")
    source_key = relationship("SourceKey", secondary=source_key_tag_assoc, back_populates="tags")

class SourceKey(Base):
    id = Column(Integer, primary_key=True, index=True)
    tags = relationship("Tag", secondary=source_key_tag_assoc, back_populates="source_key")

And here's switching to backref, which should result in the exact same as above:

class Tag(Base):
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)

class Source(Base):
    id = Column(Integer, primary_key=True, index=True)
    tags = relationship("Tag", secondary=source_tag_assoc, backref="source")

class SourceKey(Base):
    id = Column(Integer, primary_key=True, index=True)
    tags = relationship("Tag", secondary=source_key_tag_assoc, backref="source_key")

Personally I prefer back_populates, because when you have over a dozen models, it's a lot easier to see all the available relationships when they are explicitly defined.

like image 168
Peter Avatar answered Feb 04 '26 16:02

Peter



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!