Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Annotate a django query via a reverse relationship

I have two models Property and Image, related by foreign key such that each Property has multiple instances of Image. I'm trying to retrieve a queryset of all the properties - listing all fields - and the number of images that each property has. I am aware that I could do this as two distinct queries, but I don't feel that this is a particularly elegant approach, and is proving a little inefficient as this information is being retrieved via XMLHttpRequest.

The models are defined as follows:

class Property(models.Model):
    title = models.CharField('title', max_length=255)
    created = models.DateTimeField('created', auto_now_add=True)
    modified = models.DateTimeField('modified', auto_now=True)

    class Meta:
        pass


class Image(models.Model):
    prop_id = models.ForeignKey(Property)
    image_file = models.ImageField('image file', upload_to='/path/to/image/')

    class Meta:
        pass

I have followed the answer posted here: Django Aggregation Across Reverse Relationship, as I believe this was a similar problem, but I've found that this returns an empty queryset.

Thanks for any help anyone can offer.

EDIT:

The query I ran was:

Property.objects.all().annotate(image_count=Count('image')).order_by('-image_count')

EDIT 2:

After some experimentation, I have found a solution, though I'm pretty sure that this qualifies as a bug / non-documented issue:

Property.objects.all().annotate(Count('image')).order_by('-image__count')
Property.objects.all().annotate(total_images=Count('image')).order_by('-total_images')

These both work, but naming the annotation image_count did not. Without delving into the Django source, I can't really speculate as to why that's happening.

like image 277
Gary Chambers Avatar asked Jul 30 '10 08:07

Gary Chambers


People also ask

What is difference between annotate and aggregate in Django?

Unlike aggregate() , annotate() is not a terminal clause. The output of the annotate() clause is a QuerySet ; this QuerySet can be modified using any other QuerySet operation, including filter() , order_by() , or even additional calls to annotate() .

What is backward relation Django?

Every relationship in Django automatically gets its reverse relation added to the model. In the case of a ForeignKey or ManyToManyField that relation contains several objects. In that case, the default attribute name is set to <model>_set , so in this case child_set .

What is f() in Django?

An F() object represents the value of a model field, transformed value of a model field, or annotated column. It makes it possible to refer to model field values and perform database operations using them without actually having to pull them out of the database into Python memory.

What is Django annotate?

Django annotations 2 are a way of enriching the objects returned in QuerySets. That is, when you run queries against your models you can ask for new fields, whose values will be dynamically computed, to be added when evaluating the query. These fields will be accessible as if they were normal attributes of a model.


1 Answers

The code you've posted should work - in any case, it should not return an empty queryset as annotate doesn't affect the filtering of the main query.

Silly question, but are you sure there are Property elements in the database?

like image 145
Daniel Roseman Avatar answered Sep 18 '22 08:09

Daniel Roseman