OK, i was oversimplifying. if you really want a total bi-directional many-to-many relationship where the association object is essentially invisible, you have to set up proxies in both directions. The attached script illustrates an almost generic way of doing this which, after a few more iterations, could possibly become an extension to SA so that folks could re-use this approach without the ugliness. It basically sets up a "proxy" list that translates the association instances in both directions to the underlying target object. in this example, both sides of the association are many-to- many. further property tricks can be used to make one side of it scalar instead....
--~--~---------~--~----~------------~-------~--~----~ 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 [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sqlalchemy -~----------~----~----~----~------~----~------~--~---
from sqlalchemy import * m = BoundMetaData('sqlite://', echo=True) objecttable = Table('objects', m, Column('id', Integer, primary_key=True), Column('data', String(30)) ) associationtable = Table('association', m, Column('oid', Integer, ForeignKey('objects.id'), primary_key=True), Column('iid', Integer, ForeignKey('items.id'), primary_key=True)) itemtable = Table('items', m, Column('id', Integer, primary_key=True), Column('data', String(30)) ) m.create_all() class Association(object): """association object""" pass class AssociationList(object): """generic proxying list which proxies list operations to a different list-holding attribute of the parent object, converting Association objects to and from a target attribute on each Association object.""" def __init__(self, parent, collectionname, attr): """create a new AssociationList. parent - the parent object that contains this list collectionname - the attribute name which stores the collection of Associations attr - name of the attribute on the Association in which to get/set target values """ self.parent = parent self.collectionname = collectionname self.attr = attr def append(self, item): a = Association() setattr(a, self.attr, item) getattr(self.parent, self.collectionname).append(a) def __iter__(self): return iter([getattr(x, self.attr) for x in getattr(self.parent, self.collectionname)]) def __repr__(self): return repr([getattr(x, self.attr) for x in getattr(self.parent, self.collectionname)]) def __len__(self): return len(getattr(self.parent, self.collectionname)) def __getitem__(self, index): return getattr(getattr(self.parent, self.collectionname)[index], self.attr) def __setitem__(self, index, value): a = Association() setattr(a, self.attr, value) getattr(self.parent, self.collectionname)[index] = a class AssocProp(object): """a property object that automatically sets up AssociationLists on a parent object.""" def __init__(self, collectionname, attr): """create a new association property. collectionname - the attribute name which stores the collection of Associations attr - name of the attribute on the Association in which to get/set target values """ self.collectionname = collectionname self.attr = attr def __get__(self, obj, owner): if obj is None: return self storage_key = '_AssocProp_%s' % self.collectionname try: return getattr(obj, storage_key) except AttributeError: a = AssociationList(obj, self.collectionname, self.attr) setattr(obj, storage_key, a) return a class MyObject(object): items = AssocProp('associations', 'item') class Item(object): myobjs = AssocProp('associations', 'myobj') mapper(MyObject, objecttable) mapper(Item, itemtable) mapper(Association, associationtable, properties={ 'myobj':relation(MyObject, backref='associations'), 'item':relation(Item, backref='associations') }) s = create_session() o = MyObject() o.items.append(Item()) o.items.append(Item()) i = Item() i.myobjs.append(o) s.save(o) s.flush() s.clear() o = s.query(MyObject).get(o.id) print o print o.items assert len(o.items) == 3 assert o in o.items[1].myobjs
On Nov 1, 2006, at 7:09 PM, Karl Guertin wrote: > > On 11/1/06, Michael Bayer <[EMAIL PROTECTED]> wrote: >> (moving to google groups) > > Eh, sorry cached email address. > >> the idea of using an instrumented list subclass is that you *would* >> use the association object, and just have the list subclass handle >> the references to the association object for you, i.e.: > > Simple enough > >> class MyList(list): >> def append(self, item): >> super(MyList, self).append(new ItemAssociation(item)) >> def __getitem__(self, index): >> return super(MyList, self).__getitem__(index).item >> >> ..etc > > I'm not understanding what's happening in the append. I know item is > one side of my relation, but how to I get access to the other? Is the > InstrumentedList magic supposed to handle this somehow? > > --~--~---------~--~----~------------~-------~--~----~ > 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- > [EMAIL PROTECTED] > For more options, visit this group at http://groups.google.com/ > group/sqlalchemy > -~----------~----~----~----~------~----~------~--~--- >