结合使用带有Back_populates的关联对象时,SQLAlchemy抛出KeyError –文档中的示例不起作用 [英] SQLAlchemy throwing KeyError when using Association Objects with back_populates – example from documentation doesn't work
问题描述
SQLAlchemy很好地记录了如何在<$上使用关联对象c $ c> back_populates 。
SQLAlchemy nicely documents how to use Association Objects with back_populates
.
但是,当从该文档中复制并粘贴示例时,会将子级添加到父代会抛出 KeyError
,如以下代码所示。从文档中复制模型类100%:
However, when copy-and-pasting the example from that documentation, adding children to a parent throws a KeyError
as following code shows. The model classes are copied 100% from the documentation:
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy.schema import MetaData
Base = declarative_base(metadata=MetaData())
class Association(Base):
__tablename__ = 'association'
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child", back_populates="parents")
parent = relationship("Parent", back_populates="children")
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Association", back_populates="parent")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship("Association", back_populates="child")
parent = Parent(children=[Child()])
使用SQLAlchemy 1.2版运行该代码.11引发此异常:
Running that code with SQLAlchemy version 1.2.11 throws this exception:
lars$ venv/bin/python test.py
Traceback (most recent call last):
File "test.py", line 26, in <module>
parent = Parent(children=[Child()])
File "<string>", line 4, in __init__
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/state.py", line 417, in _initialize_instance
manager.dispatch.init_failure(self, args, kwargs)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/util/langhelpers.py", line 66, in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/util/compat.py", line 249, in reraise
raise value
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/state.py", line 414, in _initialize_instance
return manager.original_init(*mixed[1:], **kwargs)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/ext/declarative/base.py", line 737, in _declarative_constructor
setattr(self, k, kwargs[k])
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/attributes.py", line 229, in __set__
instance_dict(instance), value, None)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/attributes.py", line 1077, in set
initiator=evt)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/collections.py", line 762, in bulk_replace
appender(member, _sa_initiator=initiator)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/collections.py", line 1044, in append
item = __set(self, item, _sa_initiator)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/collections.py", line 1016, in __set
item = executor.fire_append_event(item, _sa_initiator)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/collections.py", line 680, in fire_append_event
item, initiator)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/attributes.py", line 943, in fire_append_event
state, value, initiator or self._append_token)
File "/Users/lars/coding/sqlalchemy_association_object_test/venv/lib/python3.7/site-packages/sqlalchemy/orm/attributes.py", line 1210, in emit_backref_from_collection_append_event
child_impl = child_state.manager[key].impl
KeyError: 'parent'
我已将其作为 SQLAlchemy问题中的错误跟踪器。
推荐答案
tldr; ,也许有人可以同时向我指出一个可行的解决方案或解决方法?必须使用关联代理扩展和为关联对象创建一个自定义构造函数,该构造函数将子对象作为第一个(!)参数。
tldr; We have to use Association Proxy extensions and create a custom constructor for the association object which takes the child object as the first (!) parameter. See solution based on the example from the question below.
SQLAlchemy的文档实际上在下一段中指出,如果我们要直接添加<$ c,我们还没有完成。将$ c> Child 模型转换为 Parent
模型,同时跳过中介 Association
模型:
SQLAlchemy's documentation actually states in the next paragraph that we aren't done yet if we want to directly add Child
models to Parent
models while skipping the intermediary Association
models:
以直接形式使用关联模式要求将
个子对象与关联实例相关联,然后再附加
给父母同样,从父级到子级的访问都通过关联对象进入
。
Working with the association pattern in its direct form requires that child objects are associated with an association instance before being appended to the parent; similarly, access from parent to child goes through the association object.
# create parent, append a child via association
p = Parent()
a = Association(extra_data="some data")
a.child = Child()
p.children.append(a)
要编写诸如问题中要求的简便代码,即 p.children = [Child()]
,我们必须使用关联代理扩展。
To write convient code such as requested in the question, i.e. p.children = [Child()]
, we have to make use of the Association Proxy extension.
以下是使用关联代理扩展的解决方案,该扩展允许将孩子直接添加到父母中,而无需在他们之间明确创建关联:
Here is the solution using an Association Proxy extension which allows to add children to a parent "directly" without explicitly creating an association between both of them:
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, relationship
from sqlalchemy.schema import MetaData
Base = declarative_base(metadata=MetaData())
class Association(Base):
__tablename__ = 'association'
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child", back_populates="parents")
parent = relationship("Parent", backref=backref("parent_children"))
def __init__(self, child=None, parent=None):
self.parent = parent
self.child = child
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = association_proxy("parent_children", "child")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
parents = relationship("Association", back_populates="child")
p = Parent(children=[Child()])
不幸的是,我只弄清楚了如何使用 backref
而不是 back_populates
,这不是现代方法。
Unfortunately I only figured out how to use backref
instead of back_populates
which isn't the "modern" approach.
特别付款注意创建自定义的 __ init __
方法,该方法将子级作为 first 参数。
Pay special attention to create a custom __init__
method which takes the child as the first argument.
这篇关于结合使用带有Back_populates的关联对象时,SQLAlchemy抛出KeyError –文档中的示例不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!