Khi nào tôi cần sử dụng sqlalchemy back_popates?


81

Khi tôi thử Ví dụ quan hệ SQLAlchemy theo hướng dẫn này: Các mẫu quan hệ cơ bản

Tôi có mã này

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

Nó hoạt động tốt, nhưng trong hướng dẫn, nó nói rằng mô hình phải là:

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

Tại sao tôi không cần back_populateshoặc backreftrong ví dụ của tôi? Khi nào tôi nên sử dụng cái này hay cái kia?

Câu trả lời:


157

Nếu sử dụng backrefbạn không cần khai báo mối quan hệ trên bảng thứ hai.

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

Nếu bạn không sử dụng backrefvà xác định các relationship'riêng biệt, thì nếu bạn không sử dụng back_populates, sqlalchemy sẽ không biết kết nối các mối quan hệ, do đó việc sửa đổi cái này cũng sửa đổi cái kia.

Vì vậy, trong ví dụ của bạn, nơi bạn đã xác định relationshipriêng của các trường, nhưng không cung cấp back_populatesđối số, việc sửa đổi một trường sẽ không tự động cập nhật trường kia trong giao dịch của bạn.

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

Hãy xem nó không tự động điền vào childrentrường như thế nào?

Bây giờ, nếu bạn cung cấp một back_populatesđối số, sqlalchemy sẽ kết nối các trường.

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

Vì vậy, bây giờ chúng tôi nhận được

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

Sqlalchemy biết hai trường này hiện có liên quan và sẽ cập nhật lẫn nhau khi trường kia được cập nhật. Cần lưu ý rằng việc sử dụng backrefcũng sẽ làm được điều này. Sử dụng back_populatesrất hay nếu bạn muốn xác định các mối quan hệ trên mọi lớp, vì vậy, thật dễ dàng để xem tất cả các trường chỉ cần nhìn lướt qua lớp mô hình, thay vì phải xem các lớp khác xác định các trường thông qua backref.


43
Một lưu ý về back_populatesvs backref: backrefngắn gọn hơn vì bạn không cần phải khai báo mối quan hệ trên cả hai lớp, nhưng trong thực tế, tôi thấy không đáng để lưu nó trên dòng. Tôi nghĩ back_populateslà tốt hơn, không chỉ bởi vì trong văn hóa python "Rõ ràng tốt hơn ẩn" (Zen của Python), mà khi bạn có nhiều mô hình, chỉ cần nhìn lướt qua phần khai báo của nó, bạn có thể thấy tất cả các mối quan hệ và tên của chúng thay vì lướt qua tất cả các mô hình liên quan. Ngoài ra, một lợi ích phụ tốt đẹp của back_populateslà bạn có được tự động hoàn tất trên cả hai hướng trên hầu hết các IDE =)
Fabiano

backref có vẻ dễ thực hiện hơn (đặc biệt là Nếu bạn chỉ rõ mối quan hệ trong cả hai lớp bằng cách sử dụng chú thích, ít nhất tôi nghĩ vậy ...), cho đến khi bạn cần triển khai nhiều mối quan hệ cho một bảng duy nhất và làm việc với các vùng chứa. Cuối cùng thì back_popates làm cho mã của bạn dễ hiểu hơn
Rhdr

parent_idthực sự cần thiết trong Child? Và những gì về bảng trợ giúp, như được chỉ ra trong tài liệu
Luiz Tauffer

1
@LuizTauffer Đây parent_idlà trường khóa ngoại thực sự lưu trữ mối quan hệ cha-con. Nó là cần thiết. Bảng trợ giúp dùng để xác định mối quan hệ nhiều-nhiều (ví dụ: nếu một Cấp độ con có thể có nhiều hơn một Cấp độ gốc). Ví dụ fkey ở trên là ví dụ một-to-manay cổ điển trong đó mỗi Con có một và chỉ một Cha mẹ, và một phụ huynh có thể có nhiều Con.
Brendan Abel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.