On Mon, Dec 5, 2011 at 6:42 PM, Michael Bayer <[email protected]> wrote: > -----BEGIN PGP SIGNED MESSAGE----- > Hash: SHA1 > > > On Dec 5, 2011, at 11:58 AM, Daniel Nouri wrote: > >> Say I have a class that derives from a Node class that has a 'parent' >> relationship (using 'parent_id' and 'id' fields): >> >> from sqlalchemy.ext.hybrid import hybrid_property >> >> class MyClass(Node): >> @hybrid_property >> def grandparent(self): >> return self.parent.parent >> >> @grandparent.expression >> def grandparent(cls): >> pass # hmm? >> >> How would I go about the implementing the grandparent hybrid >> expression? What I have is a boolean clause list with two comparisons >> of 'parent_id' and 'id' using aliases (not included in the example). >> I was hoping I could return that but it's not what it expects. >> >> Thanks for your help. > > yeah so this is where @hybrid can....not really do the whole thing. > Basically when you use a hybrid on a relationship, the @expression version > returns whatever it is that's most useful, given the context that you'd be > using it within. It of course cannot imply the automatic generation of > join() or anything like that, which is a manual thing...unless you had > @expression return a function that would process a given Query, which is > pretty nasty and not really helpful. > > So in this case, I'm not even sure @expression can return a concept that is > meaningfully the "grandparent" - usually the best it can do is return > "cls.parent", but in this case that directly contradicts that it's called > "grandparent". > > that would look like: > > q = session.query(MyClass).\ > join(parent_alias, MyClass.grandparent).\ > filter(parent_alias.grandparent == some_object) > > which is nasty because .grandparent is the same as .parent, so don't do that. > > If it returned an aliased(cls).parent, then that alias object would need to > be memoized so that you can refer to it multiple times in the query (which is > possible). That would make the usage something like: > > parent _alias = aliased(MyClass) > > q = session.query(MyClass).\ > join(parent_alias, MyClass.parent).\ > join(MyClass.grandparent, parent_alias.parent).\ > filter(MyClass.grandparent == some_node) > > which also makes little sense, so....probably don't do that either :). > > another awful thought I had, return a query option that runs it through > join(..., aliased=True): > > q = session.query(MyClass).\ > options(MyClass.grandparent).\ > filter_by(id=id_of_my_grandparent) > > I'd need to enhance options() a bit, or provide some other method to Query > like query.with_transformation(fn) that runs the query through a given > callable. > > the query above is completely opaque and pretty much can do just the one > thing it's been set up to do, not very useful :) > > or if one were to really go nuts with that approach and produce a comparator > with an __eq__() method, maybe even: > > session.query(MyClass).with_transformation(MyClass.grandparent==some_parent) > > but I consider that to be kind of a novelty. Or maybe not, > with_transformation() might be a handy method to add in any case. > > so yeah @expression.grandparent is not really very useful given the explicit > nature of the Query object, unless you seek to build tricks that build up the > Query behind the scenes.
Thanks for your explanation. I haven't used hybrid a lot yet and this helped me understand what it does and what not. Your last example that uses a 'with_transformation', i.e. a protocol that I guess would amount to allowing to run the query through an arbitrary function, is quite nice to read, and I'd definitely be a user. Then again, there's nice enough alternatives for what I'm trying to do, so I'll be fine without. :-) -- http://danielnouri.org -- 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.
