Reducing Django queries! Get all results in one query rather than multiple? - python

In my program, I am getting all items that belong to a table and are part of a certain category. This should be a really simple SQL query...but it seems like Django is making things much more complicated.
sqs = super(ItemView, self).get_queryset()
res = sqs.exclude(item_status='hidden')
return res
There are ~50 items that should be found. In the view, Django gets a queryset that is full of <SearchResult> objects. This gets sent to the front end where it is looped over and added to the page.
{% for item in items %}
{% individual_item %}
{% endfor %}
The Django toolbar is showing an individual query for every single item, so in this case, over 50 queries for what should be one.
Am I just misunderstanding how Django works and this is normal behavior? Or is there a way for me to run this as the one simple query it should be and send all the results from the view to the front end?

Related

Rendering Django Template Blocks in Specific Places on Page

I am trying to achieve something in Django Templating Language without knowing whether it is possible (without resorting to JS or something like that).
I have the following code (from Django-Oscar) which renders 3 blocks of promotions on the main page:
{# Render promotions #}
<div id="promotions">
{% for promotion in promotions_page %}
{% render_promotion promotion %}
{% endfor %}
</div>
These three blocks are Single Item, Products and New Arrivals. The code above appears in the layout.html which is essentially responsible for rendering the entire layout of the main page.
The problem is that as soon as soon as Django encounters this code, it renders all of the promotions there one after another. I, however, would like to chose where on the page I place them. What's more, I do not believe that I have much flexibility in how I render them, read - I do not want to change the Oscar e-commerce and rendering code unless absolutely necessary.
Since I have access to individual templates for Single Item, Products and New Arrivals promotions, I tried creating DTL blocks there and then calling those blocks at proper places on the layout.html. However, that does not work.
What is a proper and most efficient way of achieving what I am trying to do?
Django templating system provides features that let you add your custom templates in place of the ones that come with the apps. You don't need to patch django-oscar for this.
Start here: https://docs.djangoproject.com/fr/1.11/howto/overriding-templates/
Hope I added enlightenment.

Django template rendering speed

i'm having a performance issue while rendering a django template with a prefetch queryset witch is not really large (~1000 objects in production environment / ~200 objects in developement environment) but has several level of nesting.
Here is the prefetch :
stories = Story.objects.select_related(
'commande',
'commande__societe',
'commande__plateforme',
'client',).prefetch_related(
Prefetch('crm_message_related',
queryset=Message.objects.select_related(
'societe',
'plateforme',
'type_message').order_by('-date_received')),
Prefetch('commande__crm_feedback_related'),
Prefetch('commande__crm_guarantee_related'),
Prefetch('commande__soyouz_item_related',
queryset=Item.objects.select_related(
'etat', 'produit', 'produit__produit_enhanced', )),
Prefetch('commande__tagged_items__tag'),
Prefetch('commande__soyouz_item_related__tagged_items__tag'),
Prefetch('commande__soyouz_item_related__soyouz_annulationclient_related'),
Prefetch('commande__soyouz_item_related__soyouz_achat_related'),
Prefetch('commande__soyouz_item_related__soyouz_achat_related__soyouz_receptionachat_related'),
Prefetch('commande__soyouz_item_related__soyouz_achat_related__soyouz_annulationachat_related'),
Prefetch('soyouz_action_related'),
Prefetch('soyouz_action_related__receptionmessage__message',
to_attr='receptionmessages',
queryset=Message.objects.order_by(
'-date_received').select_related(
'societe', 'type_message', 'plateforme'))
).order_by(
'-commande__date_commande',
'-date_created'
).all().distinct()
The SQL run smoothly, thats not the issue.
The issue is when i try to render my template witch is basicaly
{% for story in stories %}
{% with items=story.commande.soyouz_item_related.all %}
{% for item in items %}
{% for tag in item.tagged_items.all %}
<button>{{ tag.tag }}</button>
{% endfor %}
{{ item.titre|safe|truncatechars:50 }}
{% endfor %}
{% endfor %}
It takes about 10 sec to display the page in production environment.
I profiled it in my development environment with django-template-timing (https://github.com/orf/django-debug-toolbar-template-timings) and indeed:
name Times Tot Time Queries Queries Time
stories.html 1 2814,1 ms 0 0 ms
3 seconds for 3 loops with 200 data (in development environment) ? It seems a lot.
Any ideas to improve the speed of template rendering ?
Thanks a lot!
Although django-template-timing reports the rendering time to be 2.8s, I suspect most of the time is consumed in executing the complex SQL query.
In Django, QuerySets are evaluated lazily. This means, the statement you posted Story.objects.select_related(...).distinct() does NOT execute the query in database. The SQL gets executed later. Since you didn't post all the code before rendering the template, I am not sure if the QuerySet gets evalueted before rendering. If not, it may be executed when iterating stories in the template:
{% for story in stories %}
If this is the case, then maybe improving your SQL can reduce the time.
You can check if SQL execution is the culprit by inserting this before rendering the template:
stories = [story for story in stories]
This iteration gets the SQL executed before rendering. After this, if django-template-timing reports a much shorter rendering time, you know SQL is the problem.
If it is indeed a template-rendering performance issue, there are some alternatives:
Use a more performant template engine, like Jinja2.
Chances are there is room for improving rendering performance with Django template: https://stackoverflow.com/a/26592207/954376.
Use a custom tag and generate the output outside the template. That will help avoid the django template overhead.

Reversing complex Admin URLs from templates

I find myself needing a bit more flexibility than what I understand I can do based on the Django documentation for reversing Admin URLs. I'm doing things like:
{% url admin:billing_creditcardtoken_add %}?customer={{ user.id }}
This works, but it feels like I should be able to do it without leaving the template tags.
If I want to find all CreditCardToken objects from the billing application that belong to the current user, I find myself doing:
{% url admin:billing_creditcardtoken %}?customer={{ user.id }}
...but this fails altogether. Is there a more elegant way of getting these URLs?
I was looking at this the wrong way. While:
{% url admin:billing_creditcardtoken_add %}?customer={{ user.id }}
...might be somewhat ugly, the only thing added syntax would serve to do is try to construct a query string, which isn't something one reverses URLs to do normally anyway. So this is an acceptable method of accomplishing this task.
What I was looking for in the second turned out to be:
{% url admin:billing_creditcardtoken_changelist %}?customer={{ user.id }}
...changelist, as it turns out, does not show a history of changes, but creates a list of possible items to change. Adding the query string applies the proper filter I needed.

How to have partially pre-rendered fields using Django-haystack

I've started using a rendered field in my django-haystack indexing to avoid database hits when a search is rendered. This is working well for the most part, but I have some runtime information (such as an edit button for staff) in the results as well that I'd like to splice in. A simplified example:
{{object.name}}<br/>
{% if user.is_staff %}
Edit
{% endif %}
{{ object.description}}
The user logic obviously can't be applied at indexing time so doesn't occur. Without using javascript hacks is there a way to splice some runtime output in amongst the pre-rendered text? I'm thinking it can be done by passing the rendered text with some formatting placeholders to a custom template tag, but I wonder if there's another way.
Edit: Perhaps multiple rendered, stored fields might be possible, covering the main fragments of the search result surrounding the logic parts, and then assembled at run time in the main results template. Would this work?

Django paging with slicing

I'm new to Django and Python and I'm trying to understand how to accomplish paging without getting all the records from a QuerySet first. All the examples I've seen with QuerySets will get all the records first like below.
tickets = Ticket.objects.filter(site=site.id)
paginator = Paginator(tickets, settings.PAGE_SIZE)
This seems to make sense so that the Paginator will have a reference to how many pages it needs. However, this seems inefficient to get all the records for each page request. I know that QuerySets can be sliced to return a range of records.
How do I design my paging in order to only get the records pertinent for that page (slicing?), yet still have paging features?
Edit: Added template code that iterates.
{% for ticket in tickets.object_list %}
{{ ticket }}<br/>
{% endfor %}
Underlying queries for the QuerySet objects are not executed until you explicitly evaluate them. Explicit evaluation occurs whenever you iterate through them, which includes calling str() or unicode().
Thus, the above code snippet is indeed efficient. The line tickets = Ticket.objects.filter(site=site.id) creates a new QuerySet, but the query isn't evaluated yet.
For the same reason, stuff like Model.objects.filter(foo).filter(bar).order_by(baz)... doesn't execute a single SQL query.

Categories

Resources