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

Reply via email to