pythondjangodjango-1.3

Django count number of times each ForeignKeyField value appears in table & retain __unicode__()


Django 1.3, Python 2.7

I have the following models (some irrelevant fields omitted):

class Encounter(models.Model):
  uuid = models.SlugField(max_length=36, unique=True, default=make_uuid, editable=False)
  created = models.DateTimeField(auto_now_add=True)
  subject = models.ForeignKey('Subject', to_field='uuid')

class Subject(models.Model): 
  given_name = models.CharField(max_length=64)
  family_name = models.CharField(max_length=64)
  system_id = models.CharField(max_length=32, blank=True)
  uuid = models.SlugField(max_length=36, unique=True, default=make_uuid, editable=False)
  def __unicode__(self):
    return u'%s, %s - %s' % (self.family_name, self.given_name, self.system_id)

I would like to know which subjects appear in my encounters table and how many times they appear. The following code does what I want with one exception:

s_in_e = Encounter.objects.values('subject').annotate(mycount=Count('subject'))

When I attempt to display the results of my annotation as follows:

{% for thing in s_in_e %}
     <p>{{ thing.subject }} was visited {{ thing.mycount }} time{{ thing.mycount|pluralize }}.</p>
{% endfor %}

It displays the subject's uuid instead of the __unicode__(). Research has shown that this is a property of values() operating on a ForeignKeyField - it only returns the id, not the object. Is there another way to calculate the number of times each subject appears in my encounters table so that I can at least display the __unicode__() value? Ideally I would have access to the entire Subject object.

I am working on what is primarily someone else's code, so I cannot edit the existing structure of the models. I might be able to add a new field to the model if necessary.


Solution

  • You can start with Subject and use the following code:

    subjects = Subject.objects.prefetch_related('encounter_set')
    

    And in your template:

    {% for subject in subjects %}
        <p> {{ subject }} was visited {{ subject.encounter_set.count }} time{{ subject.encounter_set.count|pluralize }}.</p>
    {% endfor %}
    

    prefetch_related will get you all related encounters in a single additional query. subject.encounter_set is a QuerySet, so you can use the count() method like on any other queryset.