Search code examples
djangoaggregationannotationsdisplaytag

Django aggregation get_*_display function usage


This question relates to Django Aggregation/Annotation in 1.1. Suppose I have a simple model with an IntegerField that has a "choices" parameter passed to it. In this case, it maps to a GENDERS tuple as shown. Normally, in a template (or view) I can refer to the textual value of the gender by using the get_gender_display() function.

However, when I annotate the gender counts as I do in the view below, I can't then lookup the textual value for each gender using the get_gender_display() function. See the template excerpt below. How would I go about getting the textual value for gender back?

I've never used Django Aggregation before, so maybe I'm missing an obvious solution. Thank you for considering my situation.

models.py

GENDERS = (
  ('','    '),
  (1,'Female'),
  (2,'Male'),
)

class Subscriber(models.Model):
   gender = models.IntegerField(blank=True, null=True, choices=GENDERS)

views.py

from django.db.models import Count

def myview(request):
... 
    sum_gender = Subscriber.objects.values('gender').annotate(gender_sum=Count('gender')) 
    context = { 'sum_gender':sum_gender, }
    return render_to_response(template_name, context,context_instance=RequestContext(request)) 

template

...
{% for genderAndsum in sum_genders %}
  <div>{{ genderAndsum.get_gender_display }} {{ genderAndsum.gender_sum }}</div>
{% endfor %} 
...

Solution

  • The problem is not the aggregation, it's that you're using values(). This returns a ValuesQuerySet, each of whose entries is a dictionary - not a Subscriber object. Obviously, that dict doesn't have any get_FOO_display methods.

    The quickest solution would be to use a custom filter:

    from myapp.models import GENDERS
    
    @register.filter
    def gender_lookup(value)
        gender_dict = dict(GENDERS)
        return gender_dict.get(value, '')
    

    and in the template:

    <div>{{ genderAndsum.gender|gender_lookup }} {{ genderAndsum.gender_sum }}</div>