Source code for examples.association.dict_of_sets_with_default
"""An advanced association proxy example whichillustrates nesting of association proxies to produce multi-level Pythoncollections, in this case a dictionary with string keys and sets of integersas values, which conceal the underlying mapped classes.This is a three table model which represents a parent table referencing adictionary of string keys and sets as values, where each set stores acollection of integers. The association proxy extension is used to hide thedetails of this persistence. The dictionary also generates new collectionsupon access of a non-existent key, in the same manner as Python's"collections.defaultdict" object."""importoperatorfromsqlalchemyimportColumnfromsqlalchemyimportcreate_enginefromsqlalchemyimportForeignKeyfromsqlalchemyimportIntegerfromsqlalchemyimportStringfromsqlalchemy.ext.associationproxyimportassociation_proxyfromsqlalchemy.ext.declarativeimportdeclarative_basefromsqlalchemy.ormimportrelationshipfromsqlalchemy.ormimportSessionfromsqlalchemy.orm.collectionsimportMappedCollectionclassBase(object):id=Column(Integer,primary_key=True)Base=declarative_base(cls=Base)classGenDefaultCollection(MappedCollection):def__missing__(self,key):self[key]=b=B(key)returnbclassA(Base):__tablename__="a"associations=relationship("B",collection_class=lambda:GenDefaultCollection(operator.attrgetter("key")),)collections=association_proxy("associations","values")"""Bridge the association from 'associations' over to the 'values' association proxy of B. """classB(Base):__tablename__="b"a_id=Column(Integer,ForeignKey("a.id"),nullable=False)elements=relationship("C",collection_class=set)key=Column(String)values=association_proxy("elements","value")"""Bridge the association from 'elements' over to the 'value' element of C."""def__init__(self,key,values=None):self.key=keyifvalues:self.values=valuesclassC(Base):__tablename__="c"b_id=Column(Integer,ForeignKey("b.id"),nullable=False)value=Column(Integer)def__init__(self,value):self.value=valueif__name__=="__main__":engine=create_engine("sqlite://",echo=True)Base.metadata.create_all(engine)session=Session(engine)# only "A" is referenced explicitly. Using "collections",# we deal with a dict of key/sets of integers directly.session.add_all([A(collections={"1":set([1,2,3])})])session.commit()a1=session.query(A).first()print(a1.collections["1"])a1.collections["1"].add(4)session.commit()a1.collections["2"].update([7,8,9])session.commit()print(a1.collections["2"])