Last active
August 14, 2016 11:16
-
-
Save radiosilence/8022285 to your computer and use it in GitHub Desktop.
Django Raw Annotations. I wanted to add some complex aggregates, but .extra(select=) adds your raw SQL to GROUP BY, which doesn't work for aggregates. So, I trick the annotate() function by creating my own class that pretends to be an aggregate, but really just dumps SQL. Do not use for user inputted data.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
SELECT "businesses_business"."id", "businesses_business"."user_id", "businesses_business"."slug", "businesses_business"."name", "businesses_business"."business_type_id", "businesses_business"."street", "businesses_business"."additional", "businesses_business"."town", "businesses_business"."county", "businesses_business"."postcode_id", "businesses_business"."phone_number", "businesses_business"."fax_number", "businesses_business"."email", "businesses_business"."website", "businesses_business"."strapline", "businesses_business"."description", "businesses_business"."active", "businesses_business"."deleted", "businesses_business"."activation_code", "businesses_business"."created_on", "businesses_business"."account_confirmed", "businesses_business"."preview", "businesses_business"."paid", "businesses_business"."last_updated", "businesses_business"."copy_id", "businesses_business"."logo", "businesses_business"."security_reference", "businesses_business"."video", "businesses_business"."embed_url", "businesses_business"."premium", "businesses_business"."auto_resave_code", "businesses_business"."created_during_funeral_announcement", "businesses_business"."geocode_cache", "businesses_business"."charity_no", "businesses_business"."bank_name", "businesses_business"."bank_account_no", "businesses_business"."bank_sort_code", | |
SUM("donation_donationamount"."amount") * 0.034 AS "total_charges", | |
SUM("donation_payment"."amount") AS "total_payment", | |
0.2 * COUNT("donation_donationamount"."amount") AS "total_20p", | |
SUM("donation_donationamount"."amount") - (SUM("donation_donationamount"."amount") * 0.034 + 0.2 * COUNT("donation_donationamount"."amount")) AS "total_net", | |
(SUM("donation_donationamount"."amount") - (SUM("donation_donationamount"."amount") * 0.034 + 0.2 * COUNT("donation_donationamount"."amount"))) - SUM("donation_payment"."amount") AS "total_balance", | |
SUM("donation_donationamount"."amount") AS "total_donation" | |
FROM "businesses_business" | |
INNER JOIN "businesses_businesstype" ON ("businesses_business"."business_type_id" = "businesses_businesstype"."id") | |
LEFT OUTER JOIN "donation_donationamount" ON ("businesses_business"."id" = "donation_donationamount"."business_id") | |
LEFT OUTER JOIN "donation_payment" ON ("businesses_business"."id" = "donation_payment"."business_id") | |
WHERE ("businesses_businesstype"."name" = Charity | |
AND "donation_donationamount"."id" IS NOT NULL) | |
GROUP BY "businesses_business"."id", | |
"businesses_business"."user_id", | |
"businesses_business"."slug", | |
"businesses_business"."name", | |
"businesses_business"."business_type_id", | |
"businesses_business"."street", | |
"businesses_business"."additional", | |
"businesses_business"."town", | |
"businesses_business"."county", | |
"businesses_business"."postcode_id", | |
"businesses_business"."phone_number", | |
"businesses_business"."fax_number", | |
"businesses_business"."email", | |
"businesses_business"."website", | |
"businesses_business"."strapline", | |
"businesses_business"."description", | |
"businesses_business"."active", | |
"businesses_business"."deleted", | |
"businesses_business"."activation_code", | |
"businesses_business"."created_on", | |
"businesses_business"."account_confirmed", | |
"businesses_business"."preview", | |
"businesses_business"."paid", | |
"businesses_business"."last_updated", | |
"businesses_business"."copy_id", | |
"businesses_business"."logo", | |
"businesses_business"."security_reference", | |
"businesses_business"."video", | |
"businesses_business"."embed_url", | |
"businesses_business"."premium", | |
"businesses_business"."auto_resave_code", | |
"businesses_business"."created_during_funeral_announcement", | |
"businesses_business"."geocode_cache", | |
"businesses_business"."charity_no", | |
"businesses_business"."bank_name", | |
"businesses_business"."bank_account_no", | |
"businesses_business"."bank_sort_code" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from myapp.models import RawAnnotation | |
def get_charities(data=None): | |
data = data or {} | |
totals = {} | |
charities = [] | |
# Bits of SQL | |
total_donations = 'SUM("donation_donationamount"."amount")' | |
total_charges = '{} * 0.034'.format(total_donations) | |
total_20p = '0.2 * COUNT("donation_donationamount"."amount")' | |
total_net = '{} - ({} + {})'.format(total_donations, total_charges, total_20p) | |
total_payment = 'SUM("donation_payment"."amount")' | |
total_balance = '({}) - {}'.format(total_net, total_payment) | |
charities = ( | |
Business.objects | |
.filter(business_type__name='Charity') | |
.filter(donationamount__isnull=False) | |
.annotate( | |
total_donation=Sum('donationamount__amount'), | |
total_payment=Sum('payment__amount'), | |
total_charges=RawAnnotation(total_charges), | |
total_20p=RawAnnotation(total_20p), | |
total_net=RawAnnotation(total_net), | |
total_balance=RawAnnotation(total_balance), | |
) | |
) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class RawAnnotation(object): | |
# Needs to be an existing field. Doesn't matter which. | |
lookup = 'pk' | |
def __init__(self, extra): | |
self.extra = extra | |
def add_to_query(self, query, alias, col, source, is_summary): | |
class FakeField(object): | |
def get_internal_type(self): | |
return None | |
class FakeQuery(object): | |
is_ordinal = False | |
field = FakeField() | |
is_computed = False | |
def __init__(self, sql): | |
self.sql = sql | |
def as_sql(self, *args): | |
return self.sql | |
query.aggregates[alias] = FakeQuery(self.extra) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
please help me:
i use thank hack for create raw annotate but i catch exception ValueError: too many values to unpack
why this exception raise?