SQLAlchemy throwing KeyError when using Association Objects with back_populates – example from documentation doesn't work
SQLAlchemy nicely documents how to use Association Objects with back_populates
.
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()])
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'
I've filed this as a bug in SQLAlchemy's issue tracker. Maybe somebody can point me to a working solution or workaround in the meanwhile?
python exception sqlalchemy keyerror
add a comment |
SQLAlchemy nicely documents how to use Association Objects with back_populates
.
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()])
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'
I've filed this as a bug in SQLAlchemy's issue tracker. Maybe somebody can point me to a working solution or workaround in the meanwhile?
python exception sqlalchemy keyerror
add a comment |
SQLAlchemy nicely documents how to use Association Objects with back_populates
.
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()])
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'
I've filed this as a bug in SQLAlchemy's issue tracker. Maybe somebody can point me to a working solution or workaround in the meanwhile?
python exception sqlalchemy keyerror
SQLAlchemy nicely documents how to use Association Objects with back_populates
.
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()])
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'
I've filed this as a bug in SQLAlchemy's issue tracker. Maybe somebody can point me to a working solution or workaround in the meanwhile?
python exception sqlalchemy keyerror
python exception sqlalchemy keyerror
edited Aug 30 '18 at 16:30
Lars Blumberg
asked Aug 30 '18 at 16:22
Lars BlumbergLars Blumberg
7,65785183
7,65785183
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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'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)
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()])
Unfortunately I only figured out how to use backref
instead of back_populates
which isn't the "modern" approach.
Pay special attention to create a custom __init__
method which takes the child as the first argument.
1
The custom__init__
is unnecessary, if using thecreator=
argument ofassociation_proxy
.
– Ilja Everilä
Aug 31 '18 at 8:06
Thanks @IljaEverilä, I will try that out and update my answer
– Lars Blumberg
Aug 31 '18 at 8:45
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52101595%2fsqlalchemy-throwing-keyerror-when-using-association-objects-with-back-populates%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
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'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)
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()])
Unfortunately I only figured out how to use backref
instead of back_populates
which isn't the "modern" approach.
Pay special attention to create a custom __init__
method which takes the child as the first argument.
1
The custom__init__
is unnecessary, if using thecreator=
argument ofassociation_proxy
.
– Ilja Everilä
Aug 31 '18 at 8:06
Thanks @IljaEverilä, I will try that out and update my answer
– Lars Blumberg
Aug 31 '18 at 8:45
add a comment |
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'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)
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()])
Unfortunately I only figured out how to use backref
instead of back_populates
which isn't the "modern" approach.
Pay special attention to create a custom __init__
method which takes the child as the first argument.
1
The custom__init__
is unnecessary, if using thecreator=
argument ofassociation_proxy
.
– Ilja Everilä
Aug 31 '18 at 8:06
Thanks @IljaEverilä, I will try that out and update my answer
– Lars Blumberg
Aug 31 '18 at 8:45
add a comment |
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'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)
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()])
Unfortunately I only figured out how to use backref
instead of back_populates
which isn't the "modern" approach.
Pay special attention to create a custom __init__
method which takes the child as the first argument.
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'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)
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()])
Unfortunately I only figured out how to use backref
instead of back_populates
which isn't the "modern" approach.
Pay special attention to create a custom __init__
method which takes the child as the first argument.
edited Sep 5 '18 at 19:20
answered Aug 30 '18 at 16:40
Lars BlumbergLars Blumberg
7,65785183
7,65785183
1
The custom__init__
is unnecessary, if using thecreator=
argument ofassociation_proxy
.
– Ilja Everilä
Aug 31 '18 at 8:06
Thanks @IljaEverilä, I will try that out and update my answer
– Lars Blumberg
Aug 31 '18 at 8:45
add a comment |
1
The custom__init__
is unnecessary, if using thecreator=
argument ofassociation_proxy
.
– Ilja Everilä
Aug 31 '18 at 8:06
Thanks @IljaEverilä, I will try that out and update my answer
– Lars Blumberg
Aug 31 '18 at 8:45
1
1
The custom
__init__
is unnecessary, if using the creator=
argument of association_proxy
.– Ilja Everilä
Aug 31 '18 at 8:06
The custom
__init__
is unnecessary, if using the creator=
argument of association_proxy
.– Ilja Everilä
Aug 31 '18 at 8:06
Thanks @IljaEverilä, I will try that out and update my answer
– Lars Blumberg
Aug 31 '18 at 8:45
Thanks @IljaEverilä, I will try that out and update my answer
– Lars Blumberg
Aug 31 '18 at 8:45
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52101595%2fsqlalchemy-throwing-keyerror-when-using-association-objects-with-back-populates%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown