SQLAlchemy throwing KeyError when using Association Objects with back_populates – example from documentation doesn't work










0















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?










share|improve this question




























    0















    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?










    share|improve this question


























      0












      0








      0








      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?










      share|improve this question
















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Aug 30 '18 at 16:30







      Lars Blumberg

















      asked Aug 30 '18 at 16:22









      Lars BlumbergLars Blumberg

      7,65785183




      7,65785183






















          1 Answer
          1






          active

          oldest

          votes


















          1














          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.






          share|improve this answer




















          • 1





            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










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



          );













          draft saved

          draft discarded


















          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









          1














          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.






          share|improve this answer




















          • 1





            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















          1














          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.






          share|improve this answer




















          • 1





            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













          1












          1








          1







          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.






          share|improve this answer















          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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








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












          • 1





            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







          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

















          draft saved

          draft discarded
















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          Top Tejano songwriter Luis Silva dead of heart attack at 64

          ReactJS Fetched API data displays live - need Data displayed static

          Evgeni Malkin