setting MapperProperty lazy attribute after property is created

44 views
Skip to first unread message

Kent

unread,
Mar 5, 2011, 10:37:58 AM3/5/11
to sqlalchemy
Oracle 8 strikes again. But our client's current legacy application
requires it (until we can get them off the app).

Anyway, when Oracle 8 is detected, I wish to convert certain mapper
properties' lazy attribute from False => 'subquery' because oracle 8
isn't smart enough to run the query anywhere near efficiently (but 9i
is).

So, after all the mappers are compiled (I need backrefs also), I'm
looping through the _mapper_registry and detecting which properties
need to be converted if the oracle is 8i.

Unfortunately for me:
prop.lazy = 'subquery'
prop.strategy_class = strategies.factory('subquery')

isn't enough because the prop.strategy was already initialized I
surmise.

Because of potential circular references and complications with
backrefs and future mapped classes not being mapped to a table until
after mapper() is invoked for the class, I do not think I can figure
out whether lazy should be False vs. 'subquery' during the mapper()
invocation...(at least trivially), so I am waiting until after all
mappers are compiled.

Can you think of any solutions for me? Any way to change a properties
lazy attribute after it's been instantiated? Any way to clone the
property and replace it with a new one with a difference lazy
attribute?

Thanks as always,
Kent

Michael Bayer

unread,
Mar 5, 2011, 11:04:49 AM3/5/11
to sqlal...@googlegroups.com

If it were me I'd still try to solve the problem of deciding which relationships/backrefs need the setting up front. You can limit it to those who are setting up lazy="joined" I assume, and i'd consider getting ugly too with some hardcoding, since this is for legacy support anyway.

Otherwise the internal API magic you need would be:

from sqlalchemy.orm.interfaces import StrategizedProperty

prop.strategy_class = strategies.factory('subquery')

StrategizedProperty.do_init(prop)

which would reset the "self.strategy" attribute and the collection of alternate strategies.


Kent

unread,
Mar 5, 2011, 11:18:00 AM3/5/11
to sqlalchemy
Thank you!

I don't disagree: I've been brainstorming how to work it out upfront,
but I think I'd need your topological sort to put the mappers in the
correct dependency order and since it is legacy support, I'm ok with
the non public API and potential consequences.

I had tried prop.do_init() in place of
StrategizedProperty.do_init(prop), but it failed.

Why??? (would invoke RelationshipProperty.do_init(), but I would have
guess that was the correct method instead of StrategizedProperty's)

Michael Bayer

unread,
Mar 5, 2011, 11:31:24 AM3/5/11
to sqlal...@googlegroups.com

On Mar 5, 2011, at 11:18 AM, Kent wrote:

> Thank you!
>
> I don't disagree: I've been brainstorming how to work it out upfront,
> but I think I'd need your topological sort to put the mappers in the
> correct dependency order and since it is legacy support, I'm ok with
> the non public API and potential consequences.
>
> I had tried prop.do_init() in place of
> StrategizedProperty.do_init(prop), but it failed.
>
> Why??? (would invoke RelationshipProperty.do_init(), but I would have
> guess that was the correct method instead of StrategizedProperty's)

here it is:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()

class Parent(Base):
__tablename__ = 'parent'
id = Column(Integer, primary_key=True)

children = relationship("Child", lazy='joined', backref=backref('parent', lazy='joined'))

class Child(Base):
__tablename__ = 'child'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parent.id'))

compile_mappers()

from sqlalchemy.orm import strategies, interfaces

for prop in (Parent.children.property, Child.parent.property):


prop.strategy_class = strategies.factory('subquery')

interfaces.StrategizedProperty.do_init(prop)

e = create_engine('sqlite://', echo=True)
Base.metadata.create_all(e)
s = Session(e)

s.add(Parent(children=[Child(), Child()]))
s.commit()

print "----------------------"
s.query(Parent).all()
s.close()

print "----------------------"
s.query(Child).all()

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

Kent

unread,
Mar 5, 2011, 11:44:23 AM3/5/11
to sqlalchemy
Yep, works excellent (the "Why??" I asking about why is it wrong to
invoke "prop.do_init()" instead of StrategizedProperty.do_init(prop))

Michael Bayer

unread,
Mar 5, 2011, 11:54:36 AM3/5/11
to sqlal...@googlegroups.com

On Mar 5, 2011, at 11:44 AM, Kent wrote:

> Yep, works excellent (the "Why??" I asking about why is it wrong to
> invoke "prop.do_init()" instead of StrategizedProperty.do_init(prop))

oh. because you don't want to rerun all the initialization crap in RelationshipProperty which I'm not even sure supports being run on itself twice. So just StrategizedProperty's part is run.

this of course could be a public API function, but then it falls into the why-is-1%-of-SQLA-configuration-mutable-and-not-the-other-99% issue we had like when you were mutating table.c that time.

Kent

unread,
Mar 5, 2011, 7:48:32 PM3/5/11
to sqlalchemy
Would you add a StrategizedProperty is_eager() method?

class StrategizedProperty(MapperProperty):
...
@property
def is_eager(self):
return self.lazy in (False, 'joined', 'subquery')
...


Actually, I guess it would belong as part of class
RelationshipProperty instead.

Kent
Reply all
Reply to author
Forward
0 new messages