> > Any idea how to do this? > > The new way in custom lookups is trying to first call as_vendorname(qn, > connection), if that doesn't exist then call the standard implementation, > that is as_sql(). For example on sqlite Django will first try to call > as_sqlite(), if that doesn't exist then as_sql() will be called. >
I've been following your PR and noted the change (and made some comments too) away from @add_implementation. I'm having a bit of an internal debate about how I've structured expressions with respect to as_sql though. Each expression also has a get_sql() method that as_sql() calls internally on leaf nodes. I'm thinking it'd be best for the extension point to be on get_sql(), but it'd be inconsistent with the Lookups API. The problem with overriding as_sql() would be that the extender would also need to know to recurse the entire tree which is not a good idea at all. Would the inconsistency be acceptable? When I spend some time actually trying out swappable implementations, this may all become clearer to me. > >> - Annotations that aren't aggregates. I think this should be >> extremely easy, but I might not be aware of some underlying difficulties. >> >> I don't think it is that easy. Django assumes that if any annotation >> exist, then the query is an aggregation query. But of course that doesn't >> hold if the above is done. > > You're right. I was hoping that I could just append nodes into the query.select list, but that was a dead end. I've been playing around with non-aggregate annotations today, and I think it's almost fine to just put them directly into the query.aggregates dictionary (and rename it to annotations). It works in trivial cases only, by checking the is_aggregate property. F() expressions over annotations are forced into the HAVING part. Aggregations over annotations fail, because the annotation is promoted to the select list. I think both of these problems I can overcome fairly easy, but that doesn't mean there won't be more issues. An alternative would be to special case non-aggregate annotations in a similar way that aggregates are treated in sql/query now. I don't like this idea at all, because too much needs to change. I'm trying to change as little as possible in sql/query and sql/compiler. order_by(RefSQL("case when {{author__birthdate__year}} > %s then 1 else 0 > end", [1981])). Joins generated by Django, and you can use any custom > lookups you have. No need for .extra() any more! That's quite interesting. I've already introduced the concept of a ValueNode, which is an expression that outputs whatever value you give it. Already one could write .annotate(ValueNode("case when ...")) as a string, and it'd work, but that misses the {{author__birthdate__year}} stuff in your example. Once I've got my main points implemented, I was going to experiment with something like: .annotate(Case(condition=Q(author__birthdate__year__gt=1891), true=1, false=0)) The actual use case I had in mind was in reference to #11305 (Conditional Aggregates): .annotate(Count(Case(condition=Q(author__birthdate__year__gt=1891), true=1, false=0)) But that's just speculation at the moment. More work needs to be done to see what that'd look like in annotations, but then the path is clear for implementing it with order_by and friends. Cheers, - Josh On Sunday, 12 January 2014 19:11:11 UTC+11, Anssi Kääriäinen wrote: > > On Saturday, January 11, 2014 10:06:14 AM UTC+2, Josh Smeaton wrote: >> >> >> - Custom backend implementation of aggregates and annotations >> >> Any idea how to do this? > > The new way in custom lookups is trying to first call as_vendorname(qn, > connection), if that doesn't exist then call the standard implementation, > that is as_sql(). For example on sqlite Django will first try to call > as_sqlite(), if that doesn't exist then as_sql() will be called. > > For aggregates I guess monkey-patch addition of as_vendorname() will work. > It is a bit ugly to write the custom implementations. And as the function > template for an aggregate is defined as a variable in the aggregate itself > changing that isn't going to be that easy. You can't just change the > template and call super - if the same query is executed on different > backend then things will not work. I guess what is needed is some way of > non-permanent change of the template. Possibilities seem to be addition of > template kwarg to as_sql() so you can just do: > def as_mysql(): > super(qn, connection, template='MYSQLCOUNT') > > Alternatively there could be some way to clone the aggregate into backend > specific class if needed. > >> >> - Annotations that aren't aggregates. I think this should be >> extremely easy, but I might not be aware of some underlying difficulties. >> >> I don't think it is that easy. Django assumes that if any annotation > exist, then the query is an aggregation query. But of course that doesn't > hold if the above is done. > > >> Things that should be done in the future, as I don't think there will be >> time for this just now: >> >> - Change order_by (and other functions) to accept Expressions. >> >> Not just Expressions but anything implementing query expression API. That > is, it would be very useful to be able to do > qs.order_by('birthdate__year'). And if this is done it will be actually > pretty easy to inject custom SQL into the queries. It will be relatively > easy to write a RefSQL class which allows one to do: .order_by(RefSQL("case > when {{author__birthdate__year}} > %s then 1 else 0 end", [1981])). Joins > generated by Django, and you can use any custom lookups you have. No need > for .extra() any more! > > - Anssi > -- You received this message because you are subscribed to the Google Groups "Django developers" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-developers+unsubscr...@googlegroups.com. To post to this group, send email to django-developers@googlegroups.com. Visit this group at http://groups.google.com/group/django-developers. To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/8f8bdfe7-9604-4ef5-b902-a993e3fd064a%40googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.