On Feb 15, 2008, at 11:21 PM, Eric Ongerth wrote:

>
> In case I didn't make it clear enough -- I've already done the
> following:
>
> 'children': relation(Bar,
> collection_class=attribute_mapped_collection('foo'),
>    backref=backref('parent', remote_side=[bars.c.id])) } )
>
> And that worked great -- if I only needed to have up to a SINGLE child
> per bar per foo.  Because the dict in attribute_mapped_collection
> expects scalar keys and scalar values, right?  It's not set up to
> collect a whole list of values per key.  And that is what I need.  Any
> given Foo is only going to appear once in the keys of any given Bar's
> children DictOfLists, of course.  But the values mapped to that given
> Foo need to be a list of Bars of any length.  Any given Bar will have
> 1..n children in the bars table; each of these child Bars will be
> related to a single Foo, but the total number of Foos is < n, so a
> parent Bar might have a number of child Bars for a given Foo, while
> only having zero or one single child Bar for some other Foo.
>
> There, I think that tells it more completely.  Sorry for the
> metasyntactic variables.

Im not sure you're going to be able to use the collection classes here  
- those are all built on the notion of mapped elements being  
"appended" and "removed" from a collection.....but here you are  
appending and removing further subsets of elements.  Its possible that  
you could define @appender and @remover methods that deal with the  
simple row-based data but then expose the dictionary methods in such a  
way as to group things into lists and such but i dont see how the  
collection classes really help here, they seem to be an unnecessary  
complexity since you really have to build an entire "adaptation" layer  
over the raw data.  These layers are actually quite easy to build once  
you accept that you have to do it.

My approach is below, and is my style it keeps the ORM side of things  
completely simple - the "dict of lists" "view" of the data is built up  
entirely using Python attribute and collection customizations, all  
interpreting against a simple collection of elements.   With this  
approach I'd want to build up a good suite of 20-30 unit tests to  
ensure all the expected dict/list operations are working properly.

from sqlalchemy import *
from sqlalchemy.orm import *

metadata = MetaData()

objects = Table('objects', metadata,
     Column('id', Integer, primary_key=True),
     Column('name', String(50), nullable=False)
)

object_dict_of_lists = Table('object_dict_of_lists', metadata,
     Column('id', Integer, primary_key=True),
     Column('object_id', Integer, ForeignKey('objects.id')),
     Column('key', String(50), nullable=False),
     Column('data', String(50), nullable=False)
     )

engine = create_engine("sqlite:///:memory:", echo=True)
metadata.bind = engine

metadata.create_all()

class Object(object):
     def __init__(self, name, data=None):
         self.name = name
         if data:
             self.data = data

class DictOfListElement(object):
     def __init__(self, key, data):
         self.key = key
         self.data = data

mapper(Object, objects, properties={
     '_data':relation(DictOfListElement)
})

mapper(DictOfListElement, object_dict_of_lists)

class ListAdapter(object):
     def __init__(self, parent, key):
         self.__parent = parent
         self.__key = key

     def __cached(self):
         try:
             return self.__cached_data
         except AttributeError:
             self.__cached_data = [item.data for item in  
self.__parent._data if item.key == self.__key]
             return self.__cached_data
     __cached = property(__cached)

     def __iter__(self):
         return iter(self.__cached)

     def __eq__(self, other):
         return list(self) == list(other)

     def __repr__(self):
         return repr(list(self))

     def append(self, item):
         self.__parent._data.append(DictOfListElement(self.__key, item))
         self.__cached.append(item)

     def __getitem__(self, index):
         return self.__cached[index]

     def __setitem__(self, index, value):
         self.__cached[index].data = value

     # other list like methods

class DictAdapterAttribute(object):
     def __get__(self, instance, owner):
         if instance is None:
             return self
         class DictAdapter(object):
             def __getitem__(self, key):
                 return ListAdapter(instance, key)
             def keys(self):
                 return iter(set([item.key for item in instance._data]))
             def __eq__(self, other):
                 return dict(self) == dict(other)
             def __repr__(self):
                 return repr(dict(self))

             # other dict like methods

         return DictAdapter()

     def __set__(self, instance, somedict):
         l =[]
         for key, somelist in somedict.items():
             for item in somelist:
                 l.append(DictOfListElement(key, item))
         instance._data = l

Object.data = DictAdapterAttribute()


sess = create_session()

obj1 = Object('object1', {
     'key1':['key1_one', 'key1_two', 'key1_three'],
     'key2':['key2_one', 'key2_two', 'key2_three'],
     'key3':['key3_one', 'key3_two', 'key3_three'],
})

sess.save(obj1)
sess.flush()
sess.clear()

obj1 = sess.query(Object).get(obj1.id)

assert obj1.data['key2'] == ['key2_one', 'key2_two', 'key2_three'],  
obj1.data['key2']

obj1.data['key3'].append('key3_four')
sess.flush()

sess.clear()

obj1 = sess.query(Object).get(obj1.id)

assert obj1.data == {
     'key1':['key1_one', 'key1_two', 'key1_three'],
     'key2':['key2_one', 'key2_two', 'key2_three'],
     'key3':['key3_one', 'key3_two', 'key3_three', 'key3_four'],
}

print obj1.data





--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to