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?
CodePudding user response:
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?
QuerySet
s 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.