什么时候需要使用sqlalchemy back_populates?


81

当我尝试按照本指南进行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_populatesbackref在我的示例中?什么时候应该使用其中一个?

Answers:


157

如果使用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如果您想在每个类上定义关系,则使用很好,因此很容易看到所有字段都只是浏览模型类,而不必查看通过backref定义字段的其他类。


43
关于back_populatesvs backref:的注释backref更为简洁,因为您不需要在两个类上都声明该关系,但是实际上,我发现不值得在线保存该关系。我认为back_populates更好,不仅因为在python文化中,“显式比隐式更好”(Python的Zen),而且当您有许多模型时,快速浏览一下它的声明,就可以看到所有关系及其名称,而不必翻阅所有相关模型。另外,一个不错的附带好处back_populates是,您可以在大多数IDE上双向实现自动完成=)
Fabiano

在您需要对一个表实现多个关系并使用容器之前,backref似乎更容易实现(特别是如果您使用注释在两个类中明确指出了这种关系,至少我是这样想的...)。最后,back_populates使您的代码更易于理解
Rhdr

parent_id真的有必要在孩子?以及文档中
Luiz Tauffer

1
@LuizTauffer这parent_id是存储父子关系的实际外键字段。有必要。Helper表用于定义多对多关系(例如,如果一个孩子可以有一个以上的父母)。上面的fkey示例是经典的一对多示例,其中每个子代只有一个父代,而一个父代可以有多个子代。
布伦丹·亚伯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.