association proxy documents the "proxy_factory" attribute for this purpose. see below.
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy, _AssociationSet
import operator
Base = declarative_base()
class AppenderAssociationSet(_AssociationSet):
"""subclass _AssociationSet to adapt some set methods to that of
AppenderQuery.
"""
def add(self, object_):
self.col.append(self._create(object_))
def extend(self, objects):
for obj in objects:
self.col.append(self._create(obj))
def clear(self):
""""the set assignment needs 'clear' but we dont
really have a consistent way to do that with
AppenderQuery. """
def set_factory(lazy_collection, creator, value_attr, assoc_prox):
"""Factory for associationproxy collections."""
# does "return MyObject.<value_attr>"
getter = operator.attrgetter(value_attr)
# does "MyObject.<value_attr> = <v>"
setter = lambda o, v: setattr(o, value_attr, v)
return AppenderAssociationSet(lazy_collection, creator,
getter, setter, assoc_prox)
class A(Base):
__tablename__ = "a"
id = Column(Integer, primary_key=True)
bs = relationship("B", lazy="dynamic")
cs = association_proxy("bs", "c", proxy_factory=set_factory)
class B(Base):
__tablename__ = "b"
id = Column(Integer, primary_key=True)
def __init__(self, c):
self.c = c
a_id = Column(Integer, ForeignKey('
a.id'))
c_id = Column(Integer, ForeignKey('
c.id'))
c = relationship("C")
class C(Base):
__tablename__ = "c"
id = Column(Integer, primary_key=True)
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
c1, c2, c3 = C(), C(), C()
s = Session(e)
s.add_all([
A(cs=set([c1, c2]))
])
s.commit()
a1 = s.query(A).first()
print a1.cs
a1.cs.add(c3)
s.commit()
print a1.cs.difference([c1])