Коли мені потрібно використовувати sqlalchemy back_populates?


83

Коли я пробую SQLAlchemy Relation Example, дотримуючись цього посібника: Основні зв’язки

У мене є цей код

#!/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на моєму прикладі? Коли слід використовувати той чи інший?

Відповіді:


160

Якщо ви використовуєте, 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приємно, якщо ви хочете визначити відносини для кожного класу, тому легко бачити, що всі поля просто дивляться на клас моделі, замість того, щоб дивитися на інші класи, які визначають поля за допомогою зворотного посилання.


48
Примітка про back_populatesvs backref: backrefє більш стислою, оскільки вам не потрібно оголошувати відношення в обох класах, але на практиці я вважаю, що не варто зберігати це в режимі онлайн. Я думаю, що back_populatesце краще, не тільки тому, що в культурі python "Явне краще, ніж неявне" (Zen of Python), але коли у вас багато моделей, швидким поглядом на його оголошення ви можете побачити всі відносини та їх імена, замість того, щоб переходити всі пов’язані моделі. Крім того, приємною побічною перевагою back_populatesє те, що ви отримуєте автозаповнення в обох напрямках на більшості IDE =)
Фабіано

backref може здатися легшим у реалізації (особливо якщо ви чітко вказуєте взаємозв'язок в обох класах за допомогою коментарів, принаймні я так думав ...), поки вам не доведеться реалізовувати кілька зв'язків до однієї таблиці та працювати з контейнерами. Зрештою, back_populates робить ваш код легшим для розуміння
Rhdr,

це parent_idдійсно потрібно під дитиною? А як щодо допоміжних таблиць, як зазначено в документації,
Луїз Тауфер

1
@LuizTauffer Це parent_idфактичне поле зовнішнього ключа, яке зберігає стосунки батьків і дітей. Це необхідно. Довідкові таблиці призначені для визначення відносин багато-до-багатьох (наприклад, якщо у дитини може бути більше одного з батьків). Наведений вище приклад fkey - це класичний приклад «один на один», коли кожна дитина має одного і лише одного батька, а батько може мати багато дітей.
Brendan Abel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.