Skip to content

Commit

Permalink
Support for handling non-integer gfk fk_fields, like Comments.
Browse files Browse the repository at this point in the history
  • Loading branch information
coleifer committed Jun 13, 2010
1 parent dfa9324 commit d9135d5
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 6 deletions.
7 changes: 7 additions & 0 deletions generic_aggregation/tests/models.py
Expand Up @@ -20,3 +20,10 @@ class Rating(models.Model):

def __unicode__(self):
return '%s rated %s' % (self.content_object, self.rating)


class CharFieldGFK(models.Model):
name = models.CharField(max_length=255)
object_id = models.CharField(max_length=10)
content_type = models.ForeignKey(ContentType)
content_object = GenericForeignKey(ct_field='content_type', fk_field='object_id')
21 changes: 20 additions & 1 deletion generic_aggregation/tests/tests.py
Expand Up @@ -5,7 +5,7 @@
from django.test import TestCase

from generic_aggregation import generic_annotate, generic_aggregate
from generic_aggregation.tests.models import Food, Rating
from generic_aggregation.tests.models import Food, Rating, CharFieldGFK

class SimpleTest(TestCase):
def setUp(self):
Expand Down Expand Up @@ -102,3 +102,22 @@ def test_subset_aggregation(self):

aggregated = generic_aggregate(Food.objects.all(), Rating.content_object, 'rating', models.Count, todays_ratings)
self.assertEqual(aggregated, 4)

def test_charfield_pks(self):
a1 = CharFieldGFK.objects.create(name='a1', content_object=self.apple)
a2 = CharFieldGFK.objects.create(name='a2', content_object=self.apple)
o1 = CharFieldGFK.objects.create(name='o1', content_object=self.orange)

annotated_qs = generic_annotate(Food.objects.all(), CharFieldGFK.content_object, 'name', models.Count)
self.assertEqual(annotated_qs.count(), 2)

food_a, food_b = annotated_qs

self.assertEqual(food_b.score, 1)
self.assertEqual(food_b.name, 'orange')

self.assertEqual(food_a.score, 2)
self.assertEqual(food_a.name, 'apple')

aggregated = generic_aggregate(Food.objects.all(), CharFieldGFK.content_object, 'name', models.Count)
self.assertEqual(aggregated, 3)
30 changes: 25 additions & 5 deletions generic_aggregation/utils.py
Expand Up @@ -14,6 +14,25 @@ def query_as_nested_sql(query):
else:
return query.get_compiler(connection=connection).as_nested_sql()

def gfk_expression(qs_model, gfk_field):
# handle casting the GFK field if need be
qn = connection.ops.quote_name

pk_field_type = qs_model._meta.pk.db_type()
gfk_field_type = gfk_field.model._meta.get_field(gfk_field.fk_field).db_type()

if pk_field_type == 'serial':
pk_field_type = 'integer'

if pk_field_type != gfk_field_type:
# cast the gfk to the pk type
gfk_expr = "CAST(%s AS %s)" % (qn(gfk_field.fk_field), pk_field_type)
else:
gfk_expr = qn(gfk_field.fk_field) # the object_id field on the GFK

return gfk_expr


def generic_annotate(queryset, gfk_field, aggregate_field, aggregator=models.Sum,
generic_queryset=None, desc=True):
ordering = desc and '-score' or 'score'
Expand All @@ -28,18 +47,19 @@ def generic_annotate(queryset, gfk_field, aggregate_field, aggregator=models.Sum
qn(gfk_field.model._meta.db_table), # table holding gfk'd item info
qn(gfk_field.ct_field + '_id'), # the content_type field on the GFK
content_type.pk, # the content_type id we need to match
qn(gfk_field.fk_field), # the object_id field on the GFK
gfk_expression(queryset.model, gfk_field),
qn(queryset.model._meta.db_table), # the table and pk from the main
qn(queryset.model._meta.pk.name) # part of the query
)

extra = """
sql_template = """
SELECT %s(%s) AS aggregate_score
FROM %s
WHERE
%s=%s AND
%s=%s.%s
""" % params
%s=%s.%s"""

extra = sql_template % params

if generic_queryset is not None:
generic_query = generic_queryset.values_list('pk').query
Expand Down Expand Up @@ -80,7 +100,7 @@ def generic_aggregate(queryset, gfk_field, aggregate_field, aggregator=models.Su
qn(gfk_field.model._meta.db_table), # table holding gfk'd item info
qn(gfk_field.ct_field + '_id'), # the content_type field on the GFK
content_type.pk, # the content_type id we need to match
qn(gfk_field.fk_field), # the object_id field on the GFK
gfk_expression(queryset.model, gfk_field), # the object_id field on the GFK
)

query_start = """
Expand Down

0 comments on commit d9135d5

Please sign in to comment.