Wann muss ich sqlalchemy back_populate verwenden?

81

Wenn ich SQLAlchemy Relation Example versuche, folge dieser Anleitung: Grundlegende Beziehungsmuster

Ich habe diesen Code

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

Es funktioniert gut, aber in der Anleitung heißt es, dass das Modell sein sollte:

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

Warum brauche ich nicht back_populatesoder backrefin meinem Beispiel? Wann sollte ich den einen oder anderen verwenden?

Liqang Lau
quelle

Antworten:

157

Wenn Sie verwenden, müssen backrefSie die Beziehung in der zweiten Tabelle nicht deklarieren.

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

Wenn Sie nicht verwenden backref, und die Definition der relationship‚s getrennt, dann , wenn Sie nicht verwenden back_populates, sqlalchemy wissen nicht , die Beziehungen zu verbinden, so dass man auch Modifizieren der anderen ändert.

In Ihrem Beispiel, in dem Sie die Felder relationshipseparat definiert, aber kein back_populatesArgument angegeben haben, wird durch Ändern eines Felds das andere in Ihrer Transaktion nicht automatisch aktualisiert.

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

Sehen Sie, wie es das childrenFeld nicht automatisch ausfüllte ?

Wenn Sie nun ein back_populatesArgument angeben, verbindet sqlalchemy die Felder.

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

Also jetzt bekommen wir

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

Sqlalchemy weiß, dass diese beiden Felder jetzt miteinander verbunden sind, und wird jedes Mal aktualisiert, wenn das andere aktualisiert wird. Es ist erwähnenswert, dass die Verwendung dies backrefauch tun wird. Die Verwendung back_populatesist hilfreich, wenn Sie die Beziehungen für jede Klasse definieren möchten. Daher ist es einfach zu sehen, dass alle Felder nur einen Blick auf die Modellklasse werfen, anstatt sich andere Klassen ansehen zu müssen, die Felder über backref definieren.

Brendan Abel
quelle
43
Ein Hinweis zu back_populatesvs backref: backrefist prägnanter, da Sie die Beziehung nicht für beide Klassen deklarieren müssen, aber in der Praxis finde ich es nicht wert, dies online zu speichern. Ich denke, es back_populatesist besser, nicht nur, weil in der Python-Kultur "Explizit ist besser als implizit" (Zen of Python), sondern wenn Sie viele Modelle haben, können Sie mit einem kurzen Blick auf die Deklaration alle Beziehungen und ihre Namen sehen, anstatt darüber hinwegzugehen alle verwandten Modelle. Ein netter Nebeneffekt back_populatesist auch, dass Sie bei den meisten IDEs automatisch in beide Richtungen vervollständigen =)
Fabiano
backref scheint einfacher zu implementieren zu sein (insbesondere, wenn Sie die Beziehung in beiden Klassen mithilfe von Kommentaren klar angeben, dachte ich zumindest ...), bis Sie mehrere Beziehungen zu einer einzelnen Tabelle implementieren und mit Containern arbeiten müssen. Am Ende macht back_populate Ihren Code leichter verständlich
Rhdr
ist das parent_idwirklich notwendig unter Kind? Und was ist mit Hilfstabellen, wie in der Dokumentation angegeben
Luiz Tauffer
1
@LuizTauffer Dies parent_idist das eigentliche Fremdschlüsselfeld, in dem die Eltern-Kind-Beziehung gespeichert ist. Es ist notwendig. Hilfstabellen dienen zum Definieren von Viele-zu-Viele-Beziehungen (z. B. wenn ein Kind mehr als ein Elternteil haben könnte). Das obige Beispiel ist das klassische Eins-zu-Manay-Beispiel, bei dem jedes Kind einen und nur einen Elternteil hat und ein Elternteil viele Kinder haben kann.
Brendan Abel