[sqlalchemy] Re: splitting a relation into multiple properties

2007-04-17 Thread Michael Bayer
attached is an approach that uses just the tiniest amount of  
awareness of how a mapper works, to do the whole optimized loading  
scenario ahead of time, and you get an object with your two distinct  
pro and con relationships, no list decoration or anything  
needed.  the appropriateness of this approach comes from the fact  
that your optimization case is a load-time optimization, therefore  
put the complexity at load time (which is easier to deal with since  
it doesnt have to be concerned with collection mutability).


--~--~-~--~~~---~--~~
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?hl=en
-~--~~~~--~~--~--~---

from sqlalchemy import *

class SomeClass(object):pass

class Position(object):
def __init__(self, data, type):
self.data = data
self.type = type

metadata = BoundMetaData('sqlite://', echo=True)

t1 = Table(t1, metadata, 
Column('id', Integer, primary_key=True),
Column('data', String(30)))

t2 = Table('t2', metadata, 
Column('id', Integer, primary_key=True),
Column('parent_id', Integer, ForeignKey('t1.id')),
Column('data', String(30)),
Column('type', String(20))
)
metadata.create_all()

class MyExt(MapperExtension):
def populate_instance(self, mapper, context, row, instance, identitykey, isnew):
# tell the mapper to populate the instance like it always does
mapper.populate_instance(context, instance, row, identitykey, isnew)

# get the row decorator that will translate the eager loaded row to something
# based off the columns in t2
decorator = context.attributes[id(mapper.props['_positions'].strategy)]
row = decorator(row)

# determine pro or con - create instance and append to either collection
if row[t2.c.type] == 'pro':
instance.pro.append(class_mapper(Position)._instance(context, row, None))
elif row[t2.c.type] == 'con':
instance.con.append(class_mapper(Position)._instance(context, row, None))

# alternatively!  get the instance that was just appended to _positions
# if _positions[-1].type == 'pro':
#  instance.pro.append(_positions[-1])
#  ...etc

# tell the calling mapper populate_instance is taken care of
return None

mapper(SomeClass, t1, extension=MyExt(), properties={
'_positions':relation(Position, lazy=False, viewonly=True),
'pro':relation(Position, lazy=None, primaryjoin=and_(t1.c.id==t2.c.parent_id,t2.c.type=='pro')),
'con':relation(Position, lazy=None, primaryjoin=and_(t1.c.id==t2.c.parent_id,t2.c.type=='con'))
})
mapper(Position, t2)

sess = create_session()
sc =SomeClass()
sc.pro.append(Position(tastes great, pro))
sc.pro.append(Position(less filling, pro))
sc.con.append(Position(beer sucks, con))
sess.save(sc)
sess.flush()
sess.clear()

sc = sess.query(SomeClass).get(sc.id)
assert [x.data for x in sc.pro] == [tastes great, less filling]
assert [x.data for x in sc.con] == [beer sucks]

On Apr 16, 2007, at 8:00 PM, jason kirtland wrote:


 Michael wrote:
 On Apr 12, 2007, at 2:03 PM, jason kirtland wrote:
 [...]
 This does work, but because relation updates are happening
 outside of the InstrumentedList (i.e. not on 'analysis'
 directly), I'm losing the events that would normally be
 triggered.  I don't think I can manually manage them either, as
 they're private __methods on InstrumentedList.

 some ideas which im not sure if theyd work, one is to not use
 collection_class and to go with an approach that is more like
 the   AssociationProxy - i.e. pro and con have special
 collections on   them which proxy to the utlimate associations
 colleciton, but on   top of the InstrumentedList instead of
 underneath it the way   collection_class does.

 I've got a new implementation that uses this approach and it does
 work- but it is even more complex.  If the InstrumentedCollection
 refactor (#213) allows implementors to choose when to fire add/del
 events I think this sort of pattern can be made pretty simple.

 On that front, I think it would be super useful if future
 InstrumentedCollection classes have access to their relation()
 arguments- e.g. a DRY ordered collection that keys off the
 relation's order_by.  A chance to error out at definition time
 would be useful too.

 -jek


 --~--~-~--~~~---~--~~
 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 

[sqlalchemy] Re: splitting a relation into multiple properties

2007-04-17 Thread jason kirtland

Michael wrote:
 attached is an approach that uses just the tiniest amount of
 awareness of how a mapper works, to do the whole optimized
 loading   scenario ahead of time, and you get an object with your
 two distinct   pro and con relationships, no list decoration
 or anything   needed.  the appropriateness of this approach comes
 from the fact   that your optimization case is a load-time
 optimization, therefore   put the complexity at load time (which
 is easier to deal with since   it doesnt have to be concerned
 with collection mutability).

This is great!  So much simpler!

I'm wondering, when storing partitioned instances:

 instance.pro.append(class_mapper(Position)._instance(context, 
row, None))

...does this need to go through the instrumented append() method or 
can I add them through my own, non-list method?  (When these 
mini-collection lists are backed by a collection class that manages 
ordering attributes, it's super useful to be able to differentiate 
between appends coming from from database load vs user code.)

-jek


--~--~-~--~~~---~--~~
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?hl=en
-~--~~~~--~~--~--~---



[sqlalchemy] Re: splitting a relation into multiple properties

2007-04-17 Thread Michael Bayer


On Apr 17, 2007, at 3:19 PM, jason kirtland wrote:


 Michael wrote:
 attached is an approach that uses just the tiniest amount of
 awareness of how a mapper works, to do the whole optimized
 loading   scenario ahead of time, and you get an object with your
 two distinct   pro and con relationships, no list decoration
 or anything   needed.  the appropriateness of this approach comes
 from the fact   that your optimization case is a load-time
 optimization, therefore   put the complexity at load time (which
 is easier to deal with since   it doesnt have to be concerned
 with collection mutability).

 This is great!  So much simpler!

 I'm wondering, when storing partitioned instances:

 instance.pro.append(class_mapper(Position)._instance(context,
 row, None))

 ...does this need to go through the instrumented append() method or
 can I add them through my own, non-list method?  (When these
 mini-collection lists are backed by a collection class that manages
 ordering attributes, it's super useful to be able to differentiate
 between appends coming from from database load vs user code.)

yes actually the way the eager loaders append is  something like this:

if isnew:
appender = util.UniqueAppender(l.data)
# store appender in the context
selectcontext.attributes[(instance, self.key)] = appender

then they append() to the appender, which operates on the underlying  
data.  its better that way so that events arent firing off (the two  
kinds of events currently are backrefs and session cascade operations  
for save()/update()).

you would want to store your UniqueAppender under an attribute key of  
your own and use it if one is available (or you can create it based  
on the isnew flag, which says that this parent row is the first row  
with the particular entity identity).



--~--~-~--~~~---~--~~
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?hl=en
-~--~~~~--~~--~--~---