Dybton
Dybton

Reputation: 75

Annotating data in Django Templates

I am attempting at rendering annotated data into my template

My models looks like this

class Content(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
reviews_total = models.FloatField(null=True)

def _str_(self):
    return self.title


class Review(models.Model):
    content = models.ForeignKey(Content, null=True, on_delete=models.CASCADE)
    readability = models.CharField(max_length=500)
    readability_rating = models.IntegerField()
    actionability = models.CharField(max_length=500)
    actionability_rating = models.IntegerField()
    general_comments = models.CharField(max_length=500)
    avg_rating = models.IntegerField(null=True)

And my template looks like this

{% for content in content.all %}

 <div onclick="window.location='{% url 'details' 
 content.id %}';">

{{ content.title }}
Reviews: {{ content.review_set.count }}          
Avg Rating: {{ ? }}

{% endfor %}

My view looks like this

def details(request, content_id):
    content = get_object_or_404(Content, pk=content_id)
    return render(request, 'content/details.html', {'content': content})

From the following query

x = Content.objects.annotate(avg=Avg('review__avg_rating'))
y = vars(x[pk])
y['avg'] 

Which will give me 4.0 for example.

My goal is essentially to to insert y into Avg Rating {{ }}

Is there a way similar to content.review_set.count? Or is it best to do in views?

Upvotes: 1

Views: 959

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476574

The annotation is added as an attribute to the objects in the QuerySet, so you can render this with.

So in your view, you can pass data like:

def some_view(request):
    contents = Content.objects.annotate(
        nreviews=Count('review'),
        avg=Avg('review__avg_rating')
    )
    return render(request, 'some_template.html', {'contents': contents })

and in the template, you then render this with:

{% for content in contents %}
    Reviews: {{ content.nreviews }}          
    Avg Rating: {{ content.avg }}
{% endfor %}

EDIT: If you want to show the details of one Content object, you can render this with:

def details(request, content_id):
    content = get_object_or_404(
        Content.objects.annotate(
            nreviews=Count('review'),
            avg=Avg('review__avg_rating')
        ),
        pk=content_id
    )
    return render(request, 'content/details.html', {'content': content})

and in the template:

{{ content.title }}
Reviews: {{ content.nreviews }}
Avg Rating: {{ content.avg }}

so without the {% for content in contents %} loop.

Upvotes: 3

Related Questions