So I have a a very simple (simplified) model
class MyObject(models.Model):
owning_user = models.ForeignKey(User, null=True)
Now in one of my templates I'm trying to iterate over a list of these objects to determine whether something should be displayed similar to this
{% for my_object in foo.my_object_set %}
{% if my_object.owning_user.id == user.id %}
Show Me!
{% endif %}
This works fine, but what I am finding is that the query
my_object.owning_user.id
returns every field from the owning user before getting the id which is verified both in django debug tool bar, and checking the connection queries
# django-debug-toolbar states this is repeated multiple times
SELECT ••• FROM "auth_user" WHERE "auth_user"."id" = 1
# The following test code also confirms this
from django.db import connection
conn = connection
bearing_type.owning_user.id
print conn.queries[-1]
Now since this query repeats over 1000 times and takes 2ms per query it is taking 2 seconds just to perform this - when all I care about is the id...
Is there anyway at all that I can just perform a query to get just the id from the owning_user instead of having to query all the fields?
Note, I'm trying really hard here to avoid making a raw query
If you use my_object.owning_user_id instead of my_object.owning_user, then Django will use the id instead of looking up the user.
In this case, you only need the id, but if you needed other user attributes, you could use select_related. In the view, you would do:
foo.my_object_set.select_related('user')
In the template, you don't have as much control, since you can't pass arguments.
{{ foo.my_object_set.select_related }}
Related
Rendering a page gets really slow when related objects are requested in a template.
class Item(models.Model):
name = models.CharField(max_length=160)
...
class Box(models.Model):
...
items = models.ForeignKey(Item, on_delete=models.CASCADE, null=True)
#template
{% for item in items %}
{{ item.box_set.first }}
{{ item.box_set.latest }}
{% endfor %}
Debug toolbar shows there many duplicate queries.
Why is this happening? Is there a way to speed this up?
The Django ORM has to make a request to the database when accessing a related field unless it's already cached. The main ways of caching the related objects are via select_related and prefetch_related.
What you're trying to do is a bit more difficult; you're trying to get two specific items from a collection. You can use .annotate() and Subquery to pull singular fields from a related model. This would be useful if you just wanted to display single field from box, but if you need the whole box instance this won't work.
Like you see the codes below, I'm manually using an attribute status in Store model to show my stores in HTML Templates. The problem is the more codes I make, the more repeating codes happen.
I'm trying to find a way to avoid those inefficient repetitions. Is it possible to set up in models.py to show only active stores to be shown in HTML templates?
I'm asking this because I've already done a similar thing to this. I have a string type attribute, but I need it in a list format in my templates, so I made a function to call it only with a list format. But, I can't figure out how to hide inactive stores in the same way.
Can anyone give me some suggestins for this?
models.py
class Store(models.Model):
status = models.CharField(max_length=20,
choices=(
('active', 'Active'), # ('Stored value', 'Label shown on the UI')
('inactive', 'Inactive'),
),
default='inactive')
...
HTML Templates
{% if store.status == 'active' %}
... Show store
{% else %}
... Do not show store
{% endif %}
Usually, though, People need to select only a subset of the complete set of objects. To refine the initial QuerySet, Here are the two most common ways to proceed:
Use filter() or exclude() with your queryset before sending it to the template.
filter(kwargs**)
Returns a new QuerySet containing objects that match the given lookup parameters.
active_stores = Store.objects.filter(status='active')
# send active_stores to template
or
exclude(kwargs**)
Returns a new QuerySet containing objects that do not match the given lookup parameters.
active_stores = Store.objects.exclude(status='inactive')
# send active_stores to template
In your template, you can loop through without a problem of inactive stores
{% for store in active_stores %}
{{ store }}
{% empty %}
No stores
{% endfor %}
See further explanations in the Django Documentation
Do not filter in the template language, filter in Python. From within the view function and/or class, make sure to filter the queryset:
objs = stores_queryset.filter(status='active')
Then work with the filtered objs in the template, and just iterate over objs without a conditional. It's best to keep logic out of templates entirely, always passing in the correctly prepared context data.
I have 2 models Product an Categories. between them there is a many-to-many-relation.
I have the following code:
In view:
context['products']Product.objects.all().prefetch_related(Prefetch('categories', queryset=Category.objects.only('name')))
In template:
{% for product in products %}
{{ product.categories.all.0.name}}
In this case prefetch_related is ignore, and it exeutes anew query in categories for each product.
If I remove only, it executes just 2 queries
Why ?
You can't use a queryset with only the name field, as Django will at least need the product_id to match the Category object with the Product - since you have excluded it, Django will automatically make a separate query each time to fetch it. This happens at query time, even before the object it sent to the template.
Category.objects.only('name', 'product_id') would work.
Trying to figure out Django... I'd like to build a table in a template that is largely all the data from a Django model. My preference would be to send the model queryset PersonDynamic.objects.all() to the template and have it figure out the column names and then loop through each row, adding the fields for that row. This kind of approach would give me a lot of flexibility because if the model changes the template will automatically change with it.
One approach could be to just move the column headers and each record into lists which I then send to the template but this seems less efficient than just sending the queryset.
What is the best way to do this?
Am I right about efficiency?
Does sending lists rather than model instances come with a larger risk of problems if there are a bazillion records or does that risk exist, regardless of approach?
Thanks!
Try looking at the code for django.contrib.admin, and how the built-in admin site makes tables. The admin site adds a lot of extra features to change how fields are displayed, and what fields to display, but it should give you some idea of how things work.
The absolute simplest way I can think to do what you want is to send the template a list of headers you want to use, and the queryset called with the values_list method. values_list takes a series of field names, and returns another queryset that will yield a tuple of field values instead of a model object. For example, say your PersonDynamic looks like this:
class PersonDynamic(Model):
id = AutoField()
name = CharField()
likes_python = BooleanField()
You could then send something like this to your template:
fields = ['id', 'name', 'likes_python']
qs = PersonDynamic.objects.values_list(*fields)
context = Context({'headers': fields, 'rows': qs})
template.render(context)
And your template could do:
<table>
<tr>{% for item in header %}<th>{{ item }}</th>{% endfor %}</tr>
{% for row in rows %}
<tr>{% for cell in row %}<td>{{ cell }}</td>{% endfor %}</tr>
{% endfor %}
</table>
For your other questions, this will run into problems for larger tables; it takes time and memory to loop over large lists like this, so generally a strategy like pagination is used, where only X items are displayed per page.
Say I have 3 models:
class Franchises
name = models.CharField()
class Stores
franchise = models.ForeignKey(Franchises)
name = models.CharField()
class Items
store = models.ForeignKey(Stores)
name = models.CharField()
in view
items = Items.objects.all()
in template
{% for item in items %}
<div>item.store.franchise.name</div>
{% endfor %}
I wonder whether executing item.store.franchise.name will hit the database? and what I need to do to optimize the database access?
Queries in django are lazy, meaning that they try to resolve as little as possible until accessed. So right now you will hit the database for the store reference, and then again for the franchise ref. And that would be for each item in those results (many queries).
If you know you will need those relations, you can tell the query to get them all right away:
Items.objects.selected_related('store__franchise').all()
Django will do the joins ahead of time to make sure each result is already cached for those related objects. When you hit them, it will not trigger more queries.
More info on select_related here
A really cool way to test this out is to start the django shell ./manage.py shell, and look at the queries that get issued:
from django import db
from django.db import connection
db.reset_queries()
items = list(Items.objects.all())
print connection.queries
db.reset_queries()
items = list(Items.objects.selected_related('store__franchise').all())
print connection.queries