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









On Jul 17, 2012, at 6:26 PM, Jon Parise wrote:

> I have a similar use case, and aside from introducing a duplicate "non-lazy"
> relationship to back the association_proxy, I haven't found a solution.
> 
> Does anyone have a more elegant approach?
> 
> On Saturday, February 11, 2012 12:15:38 PM UTC-8, Mark Friedenbach wrote:
> Hi, 
> 
> Is it possible to have an association_proxy (in the association object 
> pattern) that emulates a set-based collection if it goes through a 
> lazy='dynamic' relationship? I can't for the life of me find a way to 
> make this work (setting collection_class on the dynamic relationship 
> doesn't seem to do anything). 
> 
> Here's some example code of what I'm trying to do, extracted from the 
> actual project: 
> 
> class ProofOfWork(object): 
>   blocks = association_proxy('Intermediatory_nodes', 'block') 
> proof_of_work = Table('proof_of_work', db.metadata) 
> mapper(ProofOfWork, proof_of_work, properties={ 
>   'Intermediatory_nodes': relationship(lambda: Intermediatory, 
>     lazy = 'dynamic'), 
> }) 
> 
> class Block(object): 
>   proof_of_works = association_proxy('Intermediatory_nodes', 
> 'proof_of_work') 
> block = Table('block', db.metadata) 
> mapper(Block, block, properties={ 
>   'Intermediatory_nodes': relationship(lambda: Intermediatory, 
>     lazy = 'dynamic'), 
> }) 
> 
> class Intermediatory(object): 
>   pass 
> intermediatory = Table('intermediatory', db.metadata, 
>   Column('proof_of_work_id', Integer, 
>     ForeignKey('proof_of_work.id'), 
>     nullable = False), 
>   Column('block_id', Integer, 
>     ForeignKey('block.id')), 
> ) 
> mapper(Intermediatory, intermediatory, properties={ 
>   'proof_of_work': relationship(lambda: ProofOfWork, 
>     back_populates = 'Intermediatory_nodes', 
>     remote_side    = lambda: proof_of_work.c.id), 
>   'block': relationship(lambda: Block, 
>     back_populates = 'Intermediatory_nodes', 
>     remote_side    = lambda: block.c.id), 
> }) 
> 
> How can I make ProofOfWork.blocks and Block.proof_of_works return an 
> _AssociationSet instead of _AssociationList? 
> 
> Cheers, 
> Mark
> 
> -- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To view this discussion on the web visit 
> https://groups.google.com/d/msg/sqlalchemy/-/gs9rqWLKooQJ.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> To unsubscribe from this group, send email to 
> sqlalchemy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sqlalchemy?hl=en.

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to sqlalchemy@googlegroups.com.
To unsubscribe from this group, send email to 
sqlalchemy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to