Django performance with render_to_response - python

I have Django app, which have following statements:
response = render_to_response('template.html', {'s':s, 'r':r}, context_instance=RequestContext(request))
The typical statement at template.html is:
<tbody>{%for m in r.m_set.all %}<tr>
<td>{{m.id}}</td>
<td>{{m.Name}}</td>
<td>{{m.MaterialLot.id}}</td>
<td>{{m.MaterialSublot.id}}</td>
<td>{{m.Description|truncatechars:20}}</td>
<td>{{m.StorageLocation.id}} - {{m.StorageLocation.Name}}</td>
<td>{{m.Quantity|floatformat:"-3"}}</td>
<td>{{m.QuantityUnitOfMeasure.id}}</td>
<td>{{m.Status.id}}</td></tr> {%endfor%}</tbody>
There are about 10 thousands records. the page response time take about 3 minutes(ThinkServer, Linux, Apache, mod_wsgi, Python3.4, Django 1.9.4, MySQL), is this normal?
Thanks!

Every call to {{ m.XXX.id }} leads to separate SQL-query for the XXX instance. To avoid this you should use the select_related() method of the queryset.
Add the following method to the class of the r variable:
def get_all_m(self):
return self.m_set.all().select_related()
and then use it in the template:
{% for m in r.get_all_m %}

To only access the id of the related models, add the _id suffix to the field name. This data is already available because that's the value of the foreign key, so you don't need extra queries or joins. However, you are accessing the Name property of the StorageLocation field, so you need a join to prevent doing a query in every iteration.
Your call for the data becomes:
r.m_set.select_related('StorageLocation').all()
Your template becomes:
<tbody>
{%for m in r.m_set.all %}
<tr>
<td>{{m.id}}</td>
<td>{{m.Name}}</td>
<td>{{m.MaterialLot_id}}</td>
<td>{{m.MaterialSublot_id}}</td>
<td>{{m.Description|truncatechars:20}}</td>
<td>{{m.StorageLocation.id}} - {{m.StorageLocation.Name}}</td>
<td>{{m.Quantity|floatformat:"-3"}}</td>
<td>{{m.QuantityUnitOfMeasure_id}}</td>
<td>{{m.Status_id}}</td>
</tr>
{%endfor%}
</tbody>
Just remember that if you need access to a field other than id on a related model, add the field name to the select_related() call.

Related

How to query in django one to many

What I want to do?
I have an app have three models:Series,Section and Episode,every one have a one-many query(by ForeignKey). just like this
series-> many Section,section-> many Episode
Now I will show a section with episodes information in series page, but it make more query.
now code
views.py
series = Series.objects.get(id=series_id)
section = Section.objects.filter(series=series)
list.html
{% for item in sections %}
...
{% for episode in item.episode_set.all %}
...
{% endfor %}
...
{%endfor%}
models.py
class Section(models.Model):
series = models.ForeignKey(Series)
....
class Episode(models.Model):
section = models.ForeignKey(Section)
What I want to get ?
an example code tell me how to query in views.py and use just little query.
you can guess, in my code, if here are many section and many episode, it will have many query.
Some idea.
I use Laravel Before, In Laravel , it has a hasMany Function, I can use it to get other models items(Define by belongsTo).
Is Django has same function?
If in template you need only episodes, you can query just them with single query
Episode.objects.filter(section__series=series_id)
But if you need all data then you can use prefetch_related Django docs.
The simplest example looks enough for your code
series = Series.objects.filter(id=series_id).prefetch_related('section_set', 'section_set__episode_set').get()
After this calls to series.section_set.all() and to section.episode_set.all() will not produce extra query.

Get values of related models from form in template (not so much hidden filed)

i have UpdateView opening form,
url(r'^calendar/(?P<pk>[0-9]*)/update/$', UpdateView.as_view(model=Calendar,success_url='..',template_name_suffix='_update_form'),name='calendar_update'),
where one of fileds point to really long list via ForeignKey :
class Calendar(models.Model):
task = models.ForeignKey(Task,help_text=u"Task")
...
class Task(models.Model):
long_name = models.TextField(blank=True,help_text=u"")
...
which is not going to change in the form, so i want it have in the template as a hidden field (no problem so far) but also I would like to have shown its value there (but not have to get the full table, as Select list do).
I would like have it something like this:
...
<tr><td>{{ form.task.label_tag}}</td>
<td>{{ form.task.as_hidden }} {{ form.task.long_name }}</td></tr>
where long_name is field on the Task related record (TextField), but it does not work
Is there a way to get the related name without having to get the data manually and manage everything with that form in views.py?

Django Query Count Records on Row

I'm a little rusty with django.
I'm wondering if anyone can tell me how I count the db objects from this query:
UserEnteredTld = Tld.objects.filter(FKtoClient=request.user, auth=0)
I used to do:
UserEnteredTld = Tld.objects.filter(FKtoClient=request.user, auth=0).count()
In the template (with the var):
{% if UserEnteredTld %}
<h3>I have Tld's</h3>
{% endif %}
the view:
UserEnteredTld = Tld.objects.filter(FKtoClient=request.user, auth=0).count()
UserAuthTld = Tld.objects.filter(FKtoClient=request.user,auth=1).count()
return render(request, 'accounthome.html', {
'UserEnteredTld':UserEnteredTld,
'UserAuthTld':UserAuthTld
})
model
class Tld(models.Model):
auth = models.BooleanField(default=False)
FKtoClient = models.ForeignKey(User)
but, this appears to not be outputting anything. (I verified actual rows exist...)
To be clear: I'm simply trying to count the number of rows for the Tld table where a ForeignKey exists for the logged in user and the auth=0.
Don't use count for that. Use exists (docs).
user_entered_tld = Tld.objects.filter(FKtoClient=request.user, auth=False).exists()
{% if user_entered_tld %}
<h3>I have Tld's</h3>
{% endif %}
Some notes:
Django latest version is 1.7 (on beta). It's impossible you're using 2.6.2 (that seems like a Python version)
Don't camel-case your variables. Use user_entered_tld instead of
UserEnteredTld. It's a good Python convention.

How to order a set of object in a view

My code currently lists all domains in a server using:
{% for domain in server.domain_set.all %}
I want to order the domains in the view by their url. Something like:
{% for domain in server.domain_set.all().order_by('url') %}
But I get an exception "could not parse the remainder". How can I order the list?
The "could not parse the remainder" errors is because you're including Python code in your django template. Django doesn't allow that.
You could add a method on the model:
def sorted_domains(self):
return self.domain_set.all().order_by('url')
And then call it like this:
{% for domain in server.sorted_domains %}
An alternative is to set the default sort order on your Domain model with a Meta attribute.
You can use a dictsort filter:
Takes a list of dictionaries and returns that list sorted by the key
given in the argument.
{% for domain in server.domain_set.all|dictsort:'url' %}
Also see:
Sorting related items in a Django template
order_by template filter

Django - queries made repeat/inefficient

Alright, I have a Django view, like this:
#render_to('home/main.html')
def login(request):
# also tried Client.objects.select_related().all()
clients = Client.objects.all()
return {'clients':clients}
And I have a template, main.html, like this:
<ul>
{% for client in clients %}
<li>{{ client.full_name }}</li>
<ul>
{% for pet in client.pets.all %}
<li>{{ pet.full_name }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
I also print out all the queries in sql_queries at the bottom of my base template. When I run this view, the following queries are made:
SELECT `home_client`.`id`, ... FROM `home_client`;
SELECT `home_pet`.`id`, ... FROM `home_pet` WHERE `home_pet`.`client_id` = 1;
SELECT `home_client`.`id`, ... FROM `home_client` WHERE `home_client`.`id` = 1;
SELECT `home_client`.`id`, ... FROM `home_client` WHERE `home_client`.`id` = 1;
SELECT `home_pet`.`id`, ... FROM `home_pet` WHERE `home_pet`.`client_id` = 2;
SELECT `home_client`.`id`, ... FROM `home_client` WHERE `home_client`.`id` = 2;
My question is, why are all these queries being made? Shouldn't it just be 1 query to retrieve all the clients and a query per client to retrieve all the pets from each client? I have 2 clients in the home_client table, so it should be 3 queries total. Most troubling of all is that queries 3 and 4 are 100% identical. I don't want to "prematurely optimize" or anything but I do want to make sure Django isn't being wildly inefficient. Any help on this would be appreciated. Thanks.
Django uses a cache. The RDBMS uses a cache. Don't prematurely optimize the queries.
You can play with bulk queries in your view function instead of one-at-a-time queries in your template.
#render_to('home/main.html')
def login(request):
# Query all clients
clients = Client.objects.all()
# Assemble an in-memory table of pets
pets = collections.defaultdict(list)
for p in Pet.objects.all():
pets[pet.client].append(p)
# Create clients and pets tuples
clientsPetTuples = [ (c,pets[c]) for c in clients ]
return {'clientPets': clientsPetTuples}
However, you don't seem to have any evidence that your template is the slowest part of your application.
Further, this trades off giant memory use against SQL use. Until you have measurements that prove that your template queries are actually slow, you shouldn't be over thinking the SQL.
Don't worry about the SQL until you have evidence.
try using Client.objects.all().select_related()
This will automagically also cache related models in a single database query.
Does Client 1 have 2 Pets and Client 2 have 1 Pet?
If so, that would indicate to me that Pet.full_name or something else you're doing in the Pet display loop is trying to access its related Client's details. Django's ORM doesn't use an identity map, so accessing the Client foreign key from any of your Pet objects would require hitting the database again to retrieve that Client.
P.S. select_related won't have any effect on the data you're using in this scenario as it only follows foreign-key relationships, but the pet-to-client relationship is many-to-one.
Update: if you want to avoid having to change the logic in Pet.full_name or having to perform said logic in the template instead for this case, you could alter the way you get a handle on each Client's Pets in order to prefill the ForeignKey cache with for each Pet with its Client:
class Client(models.Model):
# ...
def get_pets(self):
for pet in self.pets.all():
setattr(pet, '_client_cache', self)
yield pet
...where the 'client' part of '_client_cache' is whatever attribute name is used in the Pet class for the ForeignKey to the Pet's Client. This takes advantage of the way Django implements access to ForeignKey-related objects using its SingleRelatedObjectDescriptor class, which looks for this cache attribute before querying the database.
Resulting template usage:
{% for pet in client.get_pets %}
...
{% endfor %}

Categories