SQLAlchemy nicely documents how to use Association Objects with back_populates.
However, when copy-and-pasting the example from that documentation, adding children t
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.
So to make a long story short.
You need to append an association object containing your child object onto your parent. Otherwise, you need to follow Lars' suggestion about a proxy association.
I recommend the former since it's the ORM-based way:
p = Parent()
p.children.append(Association(child = Child()))
session.add(p)
session.commit()
Note that if you have any non-nullable fields, they're easy to add on object creation for a quick test commit.