Когда мне нужно использовать sqlalchemy back_populates?

Когда я пытаюсь использовать пример привязки SQLAlchemy в следующем руководстве: Основные шаблоны отношений

У меня есть этот код

#!/usr/bin/env python
# encoding: utf-8
from sqlalchemy import create_engine
from sqlalchemy import Table, Column, Integer, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(bind=engine)
session = Session()
Base = declarative_base(bind=engine)

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    children = relationship("Child")

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parent.id'))
    parent = relationship("Parent")

Base.metadata.create_all()

p = Parent()
session.add(p)
session.commit()
c = Child(parent_id=p.id)
session.add(c)
session.commit()
print "children: {}".format(p.children[0].id)
print "parent: {}".format(c.parent.id)

Это хорошо работает, но в руководстве говорится, что модель должна быть:

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    **children = relationship("Child", back_populates="parent")**

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parent.id'))
    **parent = relationship("Parent", back_populates="children")**

Почему мне не нужны back_populates или backref в моем примере? Когда я должен использовать один или другой?

Ответы

Ответ 1

Если вы используете backref, вам не нужно объявлять отношения во второй таблице.

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    children = relationship("Child", backref="parent")

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parent.id'))

Если вы не используете backref и определяете relationship отдельно, тогда, если вы не используете back_populates, sqlalchemy не будет знать, чтобы связать отношения, так что модификация также изменяет другую.

Итак, в вашем примере, где вы определили relationship отдельно, но не указали аргумент back_populates, изменение одного поля не будет автоматически обновлять другое в вашей транзакции.

>>> parent = Parent()
>>> child = Child()
>>> child.parent = parent
>>> print parent.children
[]

Посмотрите, как он автоматически не заполнил поле children?

Теперь, если вы укажете аргумент back_populates, sqlalchemy свяжет поля.

class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    children = relationship("Child", back_populates="parent")

class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey('parent.id'))
    parent = relationship("Parent", back_populates="children")

Итак, теперь мы получаем

>>> parent = Parent()
>>> child = Child()
>>> child.parent = parent
>>> print parent.children
[Child(...)]

Sqlalchemy знает, что эти два поля связаны теперь и будут обновлять каждую, поскольку другая обновляется. Стоит отметить, что использование backref тоже сделает это. Использование back_populates приятно, если вы хотите определить отношения для каждого класса, поэтому легко увидеть, что все поля просто смотрят на класс модели, вместо того, чтобы смотреть на другие классы, которые определяют поля через backref.