Injecting custom data into Django Queryset before passing to template

Question:

What is the best way to append or inject some extra data into a Django QuerySet?

Imagine a situation where I am displaying a list of Books, and I want to show the result of a special calculation on each one:

models.py

class Book(models.Model):
    name = models.CharField(max_length=64)

book_list.html

{% for book in objects %}
    {{ book.name }} - {{ book.special_result }}
{% endfor %}

views.py

class BookListView(ListView):
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        books = self.object_list
        
        for book in books:
            book.special_result = do_special_calculation(foo=bar)

        context['books'] = books

        return context

Imagine that do_special_calculation() method cannot be calculated in the template or as a model parameter, because it needs to have a variable foo passed in.

This code does not achieve the desired result of making each book‘s special_result value accessible from the template, because the book variable is overwritten with each iteration of the for loop. Every solution I’ve come up involves basically building out a new dictionary in parallel with the QuerySet, passing that into the template, and looping through them both in the template simultaneously, causing very ugly code.

I also don’t want to save the result of do_special_calculations() back to the database for a host of reasons (efficiency, potential stale data, can’t easily save an object).

What would be the best approach to make each entry’s special calculation available in the template?

Asked By: Allen Ellis

||

Answers:

I finally solved this by making an empty list and using setattr() on each entry. Here is a fixed code example:

class BookListView(ListView):
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        books = self.object_list

        book_list = []
        
        for book in books:
            special_result = do_special_calculation(foo=bar)
            setattr(book, "special_result", special_result)
            book_list.append(book)

        context['books'] = book_list

        return context
Answered By: Allen Ellis
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.