いつsqlalchemyback_populatesを使用する必要がありますか?


83

このガイドに従って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_populatesbackref、私の例では?いつどちらを使うべきですか?

回答:


160

使用するbackref場合は、2番目のテーブルで関係を宣言する必要はありません。

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は、これら2つのフィールドが現在関連していることを認識しており、もう一方が更新されるとそれぞれを更新します。使用backrefすることでもこれができることは注目に値します。back_populatesすべてのクラスで関係を定義する場合は、を使用すると便利です。したがって、backrefを介してフィールドを定義する他のクラスを調べる代わりに、モデルクラスを一瞥するだけですべてのフィールドを簡単に確認できます。


48
back_populatesvs backref:に関する注記backrefは、両方のクラスで関係を宣言する必要がないため、より簡潔ですが、実際には、これをオンラインで保存する価値はありません。back_populatesPythonカルチャーでは「明示的は暗黙的よりも優れている」(PythonのZen)だけでなく、多くのモデルがある場合、その宣言を一目見れば、すべての関係とその名前を確認するのではなく、より良いと思います。関連するすべてのモデル。また、の優れた副次的な利点はback_populates、ほとんどのIDEで両方向のオートコンプリートが得られることです=)
Fabiano19年

backrefは、単一のテーブルに複数の関係を実装してコンテナーを操作する必要があるまで、実装が簡単に思えるかもしれません(特に、コメントを使用して両方のクラスの関係を明確に示す場合、少なくとも私はそう思いました...)。結局、back_populatesはあなたのコードを理解しやすくします
Rhdr 2010

parent_id、本当に必要なの下の子は?そして、ドキュメントに
ですか

1
@LuizTaufferparent_idは、親子関係を格納する実際の外部キーフィールドです。これは必要である。ヘルパーテーブルは、多対多の関係を定義するためのものです(たとえば、子が複数の親を持つことができる場合)。上記のfkeyの例は、各子が1つだけの親を持ち、親が多くの子を持つことができる、古典的な1対マナイの例です。
ブレンダンアベル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.