Kiedy muszę używać sqlalchemy back_populate?

84

Kiedy próbuję SQLAlchemy Przykład relacji zgodnie z tym przewodnikiem: Podstawowe wzorce relacji

Mam ten kod

#!/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)

Działa dobrze, ale w poradniku jest napisane, że model powinien być:

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

Dlaczego nie potrzebuję back_populateslub backrefna moim przykładzie? Kiedy powinienem użyć jednego lub drugiego?

Liqang Lau
źródło

Odpowiedzi:

160

Jeśli używasz backref, nie musisz deklarować relacji w drugiej tabeli.

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

Jeśli nie używasz backrefi nie definiujesz ich relationshiposobno, to jeśli nie używasz back_populates, sqlalchemy nie będzie wiedział, jak połączyć relacje, więc modyfikacja jednej modyfikuje również drugą.

Tak więc w Twoim przykładzie, w którym zdefiniowałeś relationshiposobno back_populatespola , ale nie podałeś argumentu, modyfikacja jednego pola nie spowoduje automatycznej aktualizacji drugiego w transakcji.

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

Widzisz, dlaczego nie wypełniło automatycznie childrenpola?

Teraz, jeśli podasz back_populatesargument, sqlalchemy połączy pola.

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

Więc teraz mamy

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

Sqlalchemy wie, że te dwa pola są teraz powiązane i zaktualizują się, gdy zostaną zaktualizowane. Warto zauważyć, że użycie backrefteż to zrobi. Używanie back_populatesjest przyjemne, jeśli chcesz zdefiniować relacje w każdej klasie, więc łatwo jest zobaczyć, że wszystkie pola po prostu spoglądają na klasę modelu, zamiast patrzeć na inne klasy, które definiują pola za pomocą backref.

Brendan Abel
źródło
48
Notatka o back_populatesvs backref: backrefjest bardziej zwięzła, ponieważ nie musisz deklarować relacji na obu klasach, ale w praktyce nie warto zapisywać tego w sieci. Myślę, że back_populatesjest lepsze, nie tylko dlatego, że w kulturze Pythona „Wyraźne jest lepsze niż niejawne” (Zen of Python), ale gdy masz wiele modeli, rzucając szybki rzut oka na jego deklarację, możesz zobaczyć wszystkie relacje i ich nazwy zamiast przechodzić wszystkie powiązane modele. Dodatkową zaletą back_populatesjest możliwość automatycznego uzupełniania w obu kierunkach na większości IDE =)
Fabiano
backref może wydawać się łatwiejsze do zaimplementowania (szczególnie jeśli wyraźnie wskażesz relację w obu klasach za pomocą komentarzy, przynajmniej tak mi się wydawało ...), dopóki nie będziesz musiał zaimplementować wielu relacji do jednej tabeli i pracować z kontenerami. Ostatecznie back_populate ułatwia zrozumienie kodu
Rhdr
jest parent_idnaprawdę konieczne pod dzieckiem? A co z tabelami pomocniczymi, jak wskazano w dokumentacji
Luiz Tauffer
1
@LuizTauffer To parent_idjest rzeczywiste pole klucza obcego, w którym jest przechowywana relacja rodzic-dziecko. Czy to jest to konieczne. Tabele pomocnicze służą do definiowania relacji wiele-do-wielu (na przykład, jeśli dziecko może mieć więcej niż jednego rodzica). Powyższy przykład fkey jest klasycznym przykładem indywidualnego, w którym każde dziecko ma jednego i tylko jednego rodzica, a rodzic może mieć wiele dzieci.
Brendan Abel,