我使用Flask-SQLAlchemy 2.1和SQLAlchemy 1.0.13,並且我有兩個表, Address
和Customer
有彼此多重關係,具體如下:Flask-SQLAlchemy:CircularDependencyError其中多對一關係中的同一行可以與同一表中的一對多關係
class Address(db.Model):
id = db.Column(db.Integer, primary_key=True)
... # Other rows including first_name, last_name, etc.
customer_id = db.Column(
db.Integer,
db.ForeignKey('customers.id')
)
customer = db.relationship(
'Customer',
foreign_keys=customer_id,
back_populates='addresses'
)
class Customer(db.Model):
id = db.Column(db.Integer, primary_key=True)
billing_address_id = db.Column(db.Integer, db.ForeignKey('addresses.id'))
billing_address = db.relationship(
'Address',
foreign_keys=billing_address_id
)
shipping_address_id = db.Column(
db.Integer,
db.ForeignKey('addresses.id')
)
shipping_address = db.relationship(
'Address',
foreign_keys=shipping_address_id
)
addresses = db.relationship(
'Address',
foreign_keys='Address.customer_id',
back_populates='customer'
)
還有兩個事件偵聽器,可以自動添加任何設置billing_address
或shipping_address
到addresses
爲Customer
實例:
@event.listens_for(Customer.billing_address, 'set')
def add_billing_address_event(target, value, oldvalue, initiator):
"""If a billing address is added to a `Customer`, add it to addresses."""
if value is not None and value not in target.addresses:
target.addresses.append(value)
@event.listens_for(Customer.shipping_address, 'set')
def add_shipping_address_event(target, value, oldvalue, initiator):
"""If a shipping address is added to `Customer`, add to addresses."""
if value is not None and value not in target.addresses:
target.addresses.append(value)
試圖設置Customer.billing_address
和Customer.shipping_address
導致CircularDependencyError
因爲我希望:
> c = Customer()
> c.billing_address = Address(first_name='Bill')
> c.shipping_address = Address(first_name='Ship')
> db.session.add(c)
> db.session.flush()
CircularDependencyError: Circular dependency detected. (ProcessState(ManyToOneDP(Customer.shipping_address), <Customer at 0x7f53aa5c9fd0>, delete=False), ProcessState(ManyToOneDP(Address.customer), <Address at 0x7f53aa4e4128>, delete=False), ProcessState(ManyToOneDP(Address.customer), <Address at 0x7f53aa4e4080>, delete=False), SaveUpdateState(<Customer at 0x7f53aa5c9fd0>), ProcessState(ManyToOneDP(Customer.billing_address), <Customer at 0x7f53aa5c9fd0>, delete=False), ProcessState(OneToManyDP(Customer.addresses), <Customer at 0x7f53aa5c9fd0>, delete=False), SaveUpdateState(<Address at 0x7f53aa4e4080>), SaveUpdateState(<Address at 0x7f53aa4e4128>))
如果我註釋掉的事件監聽器,這不會導致CircularDependencyError
,這也是我所期望的,因爲Customer.address
沒有被訪問。然而,這不是一種解決方案,因爲在billing_address
或shipping_address
和addresses
中存在相同的Address
實例導致的循環依賴性,我希望允許addresses
包含當前帳單和送貨地址。
按照relevant SQLAlchemy docs這應該是可以解決的,加入的關係post_update=True
參數一側,並給予它的外鍵的名稱:但
class Address(db.Model):
...
customer_id = db.Column(
db.Integer,
db.ForeignKey('customers.id', name='fk_customer_id')
)
customer = db.relationship(
'Customer',
foreign_keys=customer_id,
back_populates='addresses',
post_update=True
)
這仍然引發了CircularDependencyError
,:
CircularDependencyError: Circular dependency detected. (ProcessState(OneToManyDP(Customer.addresses), <Customer at 0x7f620af3ff60>, delete=False), SaveUpdateState(<Address at 0x7f620ae5a080>), SaveUpdateState(<Address at 0x7f620ae5a128>), ProcessState(ManyToOneDP(Customer.billing_address), <Customer at 0x7f620af3ff60>, delete=False), SaveUpdateState(<Customer at 0x7f620af3ff60>), ProcessState(ManyToOneDP(Customer.shipping_address), <Customer at 0x7f620af3ff60>, delete=False))
我也試過將use_alter=True
傳遞給customer_id
外鍵,如某些相關的StackOverflow帖子中提到的:
customer_id = db.Column(
db.Integer,
db.ForeignKey('customers.id', name='fk_customer_id', use_alter=True)
)
CircularDependencyError
仍然發生。我找到了一個似乎可行的解決方案,我將在下面發佈,但我不確定這是否是正確的解決方案。
「國旗應放在剛關係之一」時,這指的是設置'post_update = TRUE;在任一'addresses'關係,或者,兩個'shipping_address'和'billing_address'關係,而不是同一個「地址」/「客戶」關係的雙方。 – univerio