Ad

Using The Results From A Query In BOTH The Templates And The View

- 1 answer

In Django, I need to query a table to get some details from the database. I need the results BOTH in the views and in ALL of my templates.

Is there a way to only run this query once?

Here is my current setup if I don't use a context processor:

views.py:

def collect_important_information():
    return SomeModel.objects.filter(parameter=abc)

def homepage(request):
    information = collect_important_information()
    if information:
        do something
    context = {"information": information}
    return render(request, "index.html", context)

def second_page(request):
    information = collect_important_information()
    if information:
        do something else
    context = {"information": information}
    return render(request, "second.html", context)

def third_page(request):
    information = collect_important_information()
    if not information:
        do something
    context = {"information": information}
    return render(request, "third.html", context)

def fourth_page(request):
    information = collect_important_information()
    context = {"information": information}
    return render(request, "third.html", context)

There is a lot of replication here. I can reduce that by using a custom context_processor to insert the "information" variable into each template. Something like this:

custom_context_processor.py:

def site(request):
    return { "information": SomeModel.objects.filter(parameter=abc) }

This allows me to reduce my views.py to this:

def collect_important_information():
    return SomeModel.objects.filter(parameter=abc)

def homepage(request):
    information = collect_important_information()
    if information:
        do something
    return render(request, "index.html")

def second_page(request):
    information = collect_important_information()
    if information:
        do something else
    return render(request, "second.html")

def third_page(request):
    information = collect_important_information()
    if not information:
        do something
    return render(request, "third.html")

def fourth_page(request):
    return render(request, "fourth.html")

My views is much cleaner. However, if I do that I have to run the database query twice for many of these requests. Once to obtain the database variables I need inside my views.py function (for some condition that takes place, independent from the template), and a second time in the context processor file.

Is there a way to somehow 'centralize' the query and have it run only once, and still inserting it into the template file AND having it available within the function of the view?

Ad

Answer

Is there a way to somehow 'centralize' the query and have it run only once, and still inserting it into the template file AND having it available within the function of the view?

QuerySets are lazy constructing a queryset will not make a database request, you make a query to the database if you consume the queryset, for example by enumerating over it, calling str(…), repr(…) and len(…) on it, check its truthiness with bool(…) or in an if/elif clause.

If you thus use a context processor that passes the filtered queryset to the template, and you do not use the queryset in a way described above, that will not make a query. Furthermore if the data is passed to the context, it will override the data in the context processor. So you can add it to the context in case you already evaluated it in the view, to prevent making a second database query you thus override this in the context, such that there is no reference to the "other" queryset, and the result is reused.

You need to be careful not to construct a new query if the one is evaluated. For example if qs is an evaluated queryset, you should work with qs again, if you work with qs.all() this will make a new QuerySet that will then make a query to the database.

Ad
source: stackoverflow.com
Ad