1

I have this model which is related to a Node model,

class Views(models.Model):
    related_tree = models.ForeignKey(Node, on_delete=models.CASCADE, blank=True, null=True, related_name='related_views')
    views_count = models.PositiveIntegerField(null=True, blank=True, default=0)
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, blank=True, null=True)

    def __str__(self):
        return self.related_tree.name

In the template when i query a list of the Node's objects i want to aggregate the value of the 'views_count' of each object.

I have tried this in my template

{% for tree in tree_result|slice:":14" %}
{% for view in tree.related_views.all %}

{{ view.views_count }}
{% endfor %}
{% endfor %}

i have all the related count_view of the current tree but what i want to do is to aggregate the value of the related field and show it within the for loop of tree_result.

Any suggestions please ?

EDIT :

def ResultSearch(request, search):
    formatted_search = search.split('+$')
    filter = Q()
    cases = {}
    get_result_list = [x for x in search.split('+$') if x != '']
    for i, keyword in enumerate(get_result_list):
        filter |= Q(name__icontains=keyword)
        filter |= Q(Tags__icontains=keyword)
        cases['keyword_match_{}'.format(i)] = Case(
            When(name__icontains=keyword, then=1),
            default=Value(0),
            output_field=models.IntegerField(),
        )
    result = Node.objects.filter(
        filter,
        tree_type='root',
        published=True,
        tunisia_office=True
    ).annotate(
        **cases
    ).annotate(
        total_views=Sum('related_views__views_count'),
        num_bookmarks=Count('bookmarked_by'),
        keywords_matched=reduce(add, (F(name) for name in cases))
    ).order_by('-keywords_matched', '-num_bookmarks')

    context = {
    'searched_keyword': formatted_search,
    'result_list': result,
    } 

Farhani Walid
  • 927
  • 1
  • 9
  • 20

1 Answers1

2

You should annotate your queryset in the view, like:

from django.db.models import Sum

Node.objects.annotate(
    total_views=Sum('related_views__views_count')
)

The Nodes that arise from this queryset will have an extra attribute .total_views which is the sum of the views_count of the related Views.

In your template you can then render this like:

{% for tree in tree_result|slice:":14" %}
    {{ tree.total_views }}
{% endfor %}

Note: please do not aggregate in the template. Templates are not designed to implement business logic. A template should only contain "render logic". By using annotations, you will let the database do the aggregations. Databases are usually optimized to perform such tasks.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Thank you for your answer, the issue is that actually when i add {{ tree.total_views }} into the template every time i add a +1 into the view count i have in the template an increment of 4, for i.e actually i have 3 views and in the template is 16 – Farhani Walid Jul 28 '19 at 14:21
  • @FarhaniWalid: you can update this in the view as well, like `tree.related_views.update(views_count=F('views_count')+1)`. That being said, a property with side effects would probably not be the best code design. – Willem Van Onsem Jul 28 '19 at 14:24
  • db side everything seems to be ok, the increment of +1 is working but in the telmplate rendered i have an increment of 4, i have added the entire view, maybe the issue is comin from there ? – Farhani Walid Jul 28 '19 at 14:27
  • I will accept your answer and i will post anew one for the new issue that i am facing, thank you anyway – Farhani Walid Jul 28 '19 at 14:49