Can I reduce queries while iterating a RelatedManager object within a template? - django

This is a hypothetical for simplicity. In my Django app I have models for Kit, KitSku, and Sku. The KitSku model associates a Sku with a Kit and also provides the quantity of that Sku in that kit. In the template I have something like:
<!-- SELECT * FROM kitsku_table WHERE kit_id = <Kit.id> -->
{% for kitsku in kit.kitsku_set.all %}
<!-- SELECT * FROM sku_table WHERE sku = <KitSku.sku> -->
<div>{{ kitsku.sku.name }}</div>
{% endfor %}
Now, the problem here is that Django is querying all of the KitSku rows and then it queries each sku within that for loop in a separate SQL query for each iteration.
Can I make the SQL query resulting from the kitsku_set.all() call perform a JOIN with the Sku model?
That first query needs to be more like:
SELECT * FROM kitsku_table k LEFT JOIN sku_table s ON (k.sku = s.sku)
WHERE k.kit_id = <Kit.id>

Do this type of logic in the view, and use select_related() to directly query the foreign keys
kitskus = Kit.objects.get(id=3).kitsku_set.select_related('sku')
return direct_to_template(request, "mytemplate.html", {'kitskus': kitskus})

Im just copying an answer for a similar question, but i think this is a much better approach.
view
newsletters = Newsletter.objects.prefetch_related('article_set').all()\
.order_by('-year', '-number')
return render_to_response('newsletter/newsletter_list.html',
{'newsletter_list': newsletters})
template
{% block content %}
{% for newsletter in newsletter_list %}
<h2>{{ newsletter.label }}</h2>
<p>Volume {{ newsletter.volume }}, Number {{ newsletter.number }}</p>
<p>{{ newsletter.article }}</p>
<ul>
{% for a in newsletter.article_set.all %}
<li>{{ a.title }}</li>
{% endfor %}
</ul>
{% endfor %}
{% endblock %}
Here is the complete explanation:
Hopes it helps
Iterating over related objects in Django: loop over query set or use one-liner select_related (or prefetch_related)

Related

Django querysets. How to prefetch only unique?

Models:
class House(Model)
class Flat(Model):
house = ForeignKey(House, related_name="houses")
owner = ForeignKey(User)
class User(Model)
queryset:
queryset = User.objects.prefetch_related(
Prefetch("flats", queryset=Flat.objects.select_related("houses"))
And then flats:
{% for flat in user.flats.all %}
<p>№ {{ flat.number }}, {{ flat.house.name }}</p>
{% endfor %}
It's fine. But for houses I need only unique ones
{% for flat in user.flats.all %}
<p>House {{ flat.house.name }}</p>
{% endfor %}
But this template gives me ALL houses, with duplicates.
How can I avoid duplicates, any ideas? I tried .distinct() but it's dosen't work, looks like I using distinct() wrong or etc.
It seems you may end up with duplicate houses if a user is the owner of multiple flats, which are all in the same house. To get all houses that the user is related to via Flats, you can use a different query, starting with House so that you can use distinct:
distinct_houses = House.objects.filter(flats__owner=user).distinct()
The above returns one House object for each flat where the user is an owner, and makes sure you don't have duplicates. (Note that the above assumes the related_name is changed to flats for house = ForeignKey(House, related_name="flats"), since "houses" as the related_name seems like a typo to relate back to the Flat model.)
Alternatively, you could do something in-memory in Python, if you still also need to know all Flats via your original queryset = User.objects.prefetch_related(...) and don't want an additional query. Like:
distinct_houses = {flat.house for flat in user.flats.all()}
Side note: it looks like you have a typo in needing to use Flat.objects.select_related('house') rather than Flat.objects.select_related('houses') based on the Foreign Key field name being house.
Only found solution with template filters.
Queryset:
queryset = User.objects.filter(status="ACTIVE").prefetch_related(
Prefetch("flats", queryset=Flat.objects.select_related("house"))
)
With this I can get all flats for each user:
{% for user in object_list %}
{% for flat in user.flats.all %}
{{ flat.number }}, {{ flat.house.name }}
{% endfor %}
{% endfor %}
And I am using template filter to get unique houses.
{% for user in object_list %}
{% for flat in user.flats.all|get_unique_houses %}
{{ flat.house.name }}
{% endfor %}
{% endfor %}
# extra_tags.py
#register.filter(name="get_unique_houses")
def get_unique_houses(value):
return value.distinct('house')

Django ListView how loop in one field in the template?

I have django ListView I know I can loop in it like this
{% for i in object_list %}
{% i.id %}
{% endfor %}
I want to loop in one field only
{% for i in object_list.id %}
{% i %}
{% endfor %}
update:
my model:
class Weather(models.Model):
temperature = models.FloatField(
validators=[MaxValueValidator(28), MinValueValidator(19)]
)
humidity = models.FloatField(
validators=[MaxValueValidator(65), MinValueValidator(35)]
)
time_recorded = models.DateTimeField(auto_now_add=True, blank=True)
ListView:
class WeatherListView(ListView):
template_name = "frontend/weather_list.html"
model = Weather
I want to loop for each of these fields in different position in the html template so I don't want to loop in the whole list each time for instance if I want to loop in temperature only:
I want to do this:
{% for i in object_list.temperature %}
{% i %}
{% endfor %}
instead of:
{% for i in object_list %}
{% i.temperature %}
{% endfor %}
OK, I think there is a misunderstanding on what object_list in the template represents. You are not looping over the "whole context". object_list is a variable in the context representing the QuerySet of the particular view.
In the case of WeatherListView it's Weather.objects.all(). So when you write {% for i in object_list %} you are looping over all the Weather instances in the database.
I'm not sure what you are trying to optimize. Perhaps it's the amount of database queries? In that case I can tell you that QuerySets in Django are evaluated lazily, but once they are evaluated the result is cached. So if you loop over object_list once the next time you loop over it will not result in a new database query.

Django how to list model objects date wise

I have a model from which I need to list all the objects from a given time period.
For example: 10-05-2014 to 10-12-2014
And then I need to display the objects date wise. Meaning first display objects created on the start_date(10-05-2014) up until end_date(10-12-2014)
For example:
10-05-2014:
objects list for that day
11-05-2014:
objects list for that day
and so on until end_date
Query:
MyModel.objects.unsettled.filter(
created_on__range=[start_date, end_date]
)
But my problem is how do I list the query set in increasing order by date wise in my template. So that all the objects created on same date will be shown under that date. In short I want to display the list in sections divided by date.
MyModel.objects.unsettled.filter(created_on__range=[start_date, end_date]).order_by("created_on"). But it will just sort the list. How do I group the results.??
Use the {% ifchanged %} template tag:
{% for obj in obj_list %}
{% ifchanged %}
<h2>{{ obj.created_on|date }}</h2>
{% endifchanged %}
<div>{{ obj.name }}</div>
{% endfor %}
Another (slightly more complex) option is to use the {% regroup %} tag.

reduce django db calls

I noticed that my django code calls my database very often with the exact same queries.
I understand that a db hit is made when I actually need the data to display on a page or to evaluate. However, my template code looks like this:
template:
{% if item.listing %}
{{ item.name }} text <strong>{{ item.listing|lowestprice }}</strong> more text
{% else %}
{{ item.name }} even more text
{% endif %}
....
{% for listed_item in item.listing %}
....
{% endfor %}
custom filter:
def lowestprice(value):
try:
val = unicode(value[0].price) + unicode(value[0].symbol)
return val
except:
return "not available"
This code hits my db three times. First on template {% if .. %} second on my custom filter, third on the {% for %} loop.
listing is a method of my models class which is returning a raw SQL queryset with some very expensive joins.
def listing(self):
return Universe.objects.raw("ONE HELL OF A QUERY")
How can I reduce my code to hit the db only once?
Edit: Using with works, but is it possible to avoid db hits on custom filters?
You should use with to do the expensive query once and store it the context.
{% with item.listing as item_listing %}
{% if item_listing %} ... {% endif %} ... etc ...
{% endwith %}

Using django-haystack, how do I order results by content type

I'm using django-haystack for a search page on my site, and I want to order all the results by their content type. Is there a way I can do that?
To make it simpler, suppose I have a single application and several classes.
Thanks in advance
Not sure what you mean by content type, but if you are talking about group by models, I have this working
{% with page.object_list as results %}
{% regroup results|dictsort:"verbose_name_plural" by verbose_name_plural as grouped_objects %}
{% for ct in grouped_objects %}
{{ ct.grouper }}
{% for result in ct.list %}
<p>
{{ result.object }}
</p>
{% endfor %}
{% empty %}
<p>No results found.</p>
{% endfor %}
{% endwith %}
from How to order search results by Model:
You can do
SearchQuerySet().order_by('django_ct').
As a warning, this throws out
relevancy. The only way to keep
relevancy & group by model is either
to run many queries (one per model -
usually performant thanks to query
caching) or to run the query and
post-process the results, regrouping
them as you go.
from searchindex api:
Haystack reserves the following field
names for internal use: id, django_ct,
django_id & content. The name & type
names used to be reserved but no
longer are.
You can override these field names
using the HAYSTACK_ID_FIELD,
HAYSTACK_DJANGO_CT_FIELD &
HAYSTACK_DJANGO_ID_FIELD if needed.