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?

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.


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,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.