Iterate through an object in Django template - django

In Django 1.9, I have a database table that contains car brands. I am trying to build an index (like one found at the back of a textbook) of car brands. For example:
A
Aston Martin
Audi
...
B
Bentley
BMW
...
Here is code from my view.py:
def home(request):
car_index = {}
alphabet = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N',
'O','P','Q','R','S','T','U','V','W','X','Y','Z']
for letter in alphabet:
car_index[letter] = {}
car_index[letter]['title'] = letter
car_index[letter]['brands'] = Cars.objects.filter(brand__startswith = letter)
return render(request, 'cars/home.html', {'car_index':car_index})
Here is code from my home.html template:
{% for each in car_index %}
{{ each.title }}
{% for brand in each.brands %}
{{ brand.name }}<br>
{% endfor %}
{% endfor %}
In view.py, I have tried .values() in the queryset, .items() in the template context. In the template, I have tried car_index.items, each.brands.items, each.brands[0]. Nothing works. With the code above as is, I get the titles: E D X I A U H B T S N K Q Z J Y W V O L R F G P C M, but no links. (I know how to sort, but working on links first)
I have read:
https://docs.djangoproject.com/en/1.9/ref/templates/builtins/#for
how to iterate through dictionary in a dictionary in django template?

better approach - cleaner code, apart from db effeciency:
alphabet = ['A','B','C','D','E','F','G', ..]
brands_list = []
for letter in alphabet:
letter_ = {'cars': Cars.objects.filter(brand__startswith=letter), 'letter': letter}
brands_list.append(letter_)
return render(request, 'cars/home.html', {'brands': brands_list})
template
{% for brand in brands %}
{{ brand.letter }}
{% for car in brand.cars %}
{{ car.name }}<br>
{% endfor %}
{% endfor %}

Can I dissuade you from your current approach?
You are doing a full table scan in 26 steps. If the brand column is not unique, you will find that the names get repeated. If you have millions of records you will exhaust memory. If the brand column does not have an index, you will find that the following query is very very slow:
Cars.objects.filter(brand__startswith = letter)
There is a really simple solution. That too might involve a full table scan, but at least you are executing one slow query instead of 26.
Cars.objects.raw('SELECT max(id), SubStr(brand,1,1) AS letter, brand
FROM myapp_cars GROUP BY substr(brand,1,1)')
This is with the use of raw queries. If you don't like them and happen to be fortunate enough to be on Postgresql you can use distinct to achieve the same objectively more elegantly.

I'm glad my comment helped you somehow to solve your problem.
I'm just posting it as an answer so that it may help others .
{% for each in car_index %}, just iterates out the current dict and doesn't unpack the dict of dicts which is the brands dict
As the car_index context contained a dictionary, the following would display the keys and values of the car_index dictionary and unpacks the brands dict.
{% for key,value in car_index.items %}
{{ value.title }}
{% for brand in value.brands %}
{{ brand.name }}<br>
{% endfor %}
{% endfor %}

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')

Annotate results with foreignkey

I'd like to filter an object in Django, and return it along with any objects for which the results are a ForeignKey. I'm not sure if annotation is what I need here, or something else.
This is my model:
class Dress(models.Model):
name = models.CharField(...)
price = models.FloatField(...)
class ClothingSize(models.Model):
code = models.CharField(...)
class DressSizeAvailable(models.Model):
dress = models.ForeignKey(Dress)
size = models.ForeignKey(ClothingSize)
Say I want to filter all dresses under $50, and then display them with the available sizes. What is the best way to do this?
I've got as far as this:
# In view
results = Dress.objects.filter(price__lt=50)
# How to annotate with available sizes?
# In template
{% for result in results %}
{{ result.name }}, ${{ result.price }}
Sizes: {{ ??? }}
{% endfor %}
### 1st solution (similar to Daniel Rosenthal's answer but with performances concern)
results = DressSizeAvailable.objects.filter(dress__price__lt=50).select_related('dress', 'size')
# then you can browse your DressSizeAvailables as you wish in your template. The select related allows you NOT TO query the database again when you generate your template
### 2nd solution
results = Dress.objects.filter(price__lt=50).prefetch_related('dresssizeavailable_set__dress')
{% for dress in results %}
{{ dress.name }}, ${{ dress }}
Sizes: <ul>
{% for dsa in dress.dresssizeavailable_set %}
<!-- Thanks to the prefetch_related, we do not query the base again here -->
<li>{{ dsa.size.code }}</li>
{% endfor %}
</ul>
{% endfor %}
You'll need to give your template a queryset (or something similar) of DressSizeAvailable objects. So first you'll need to get all the Dress objects you're interested, and then filter DressSizeAvailable based on those Dresses
so in your view:
dresses = Dress.objects.filter(price__lt=50)
dress_size_available = []
for dress in dresses:
available = DressSizeAvailable.objects.filter(dress=dress)
dress_size_available.append(available)
Then render your template, passing it the variable dress_size_available
Your template can then be rather simple.
{% for dsa in dress_size_available %}
{{ dsa.dress.name }}, ${{ dsa.dress.price }}
Size: {{ dsa.size }}
{% endfor %}

Django template variables as key for a list

I'm coming from this question Use variable as dictionary key in Django template
i've this context created in my view:
{'cats': {'LIST1': ['a','b','c'], 'LIST2': ['aa','bb','cc','dd'], 'LIST3': ['f','g']}}
what i want to do is to print the list title and then all the items, something like
LIST1:
- a
- b
- c
for all the lists, so i did this in my template
{% for l_name, l in cats %}
{{ l_name }}
{%for lv in l %}
{{ lv }}
{% endfor %}
{% endfor %}
and this prints only the list names, without printing out the list. where's the mistake?
thanks
If you want to iterate over keys and values, you can use:
{% for name, lst in cats.iteritems %}
....
{% endfor %}
This just calls the iteritems method of a dictionary, which returns an iterator over a list of 2-tuples.
Django's {% for %} tag documentation has some nice example of this too.
Just for the record, the problem is how the data are created. so instead of
{'cats': {'LIST1': ['a','b','c'], 'LIST2': ['aa','bb','cc','dd'], 'LIST3': ['f','g']}}
i need a list of touple so:
{'cats': [('LIST1', ['a','b','c']), ('LIST2', ['aa','bb','cc','dd']), ('LIST3', ['f','g'])]}

Determine order of iteration in django template for loop

I am populating a list in my view:
hits_object = {}
hits_object['Studio'] = hitlist('Studio',days)
hits_object['Film'] = hitlist('Film',days)
hits_object['Actor'] = hitlist('Actor',days)
hits_object['Historical Event'] = hitlist('Event',days)
hits_object['Pop Culture'] = hitlist('Pop_Culture',days)
Then I am displaying it in my template:
{% for model, hits in hits_object.items %}
{% if hits %}
<u> Most {{ model }} views in last {{ days }} days</u>
<ol>
{% for hit in hits %}
<li>{{ hit.name }} - {{ hit.count }}</li>
{% endfor %}
</ol>
</u>
{% endif %}
{% endfor %}
The problem is that the models display in a seemingly random order: first Actor, then Studio, Historical Event, Film, etc.
How can I force the for loop in the template to iterate the object in a specific order?
Dictionaries are unordered. If you need to preserve insertion order, use an ordered dict implementation - there's one in django.utils.datastructures.SortedDict, for example.
Or, since you don't seem to be using the key of the dictionary but are just iterating through, appending to a simple list would seem to be easier.
As Daniel explained, dictionaries are accessed randomly. Here is one way to do what you want:
hits_object = list()
hits_objects.append(
(hitlist('Studio',days),
hitlist('Film',days),
hitlist('Actor',days),
hitlist('Event',days),
hitlist('Pop_Culture',days))
In your view:
{% for studio,film,actor,event,pop_culture in hits_objects %}
# do something...
{% endfor %}

Use variable as dictionary key in Django template

I'd like to use a variable as an key in a dictionary in a Django template. I can't for the life of me figure out how to do it. If I have a product with a name or ID field, and ratings dictionary with indices of the product IDs, I'd like to be able to say:
{% for product in product_list %}
<h1>{{ ratings.product.id }}</h1>
{% endfor %}
In python this would be accomplished with a simple
ratings[product.id]
But I can't make it work in the templates. I've tried using with... no dice. Ideas?
Create a template tag like this (in yourproject/templatetags):
#register.filter
def keyvalue(dict, key):
return dict[key]
Usage:
{{dictionary|keyvalue:key_variable}}
You need to prepare your data beforehand, in this case you should pass list of two-tuples to your template:
{% for product, rating in product_list %}
<h1>{{ product.name }}</h1><p>{{ rating }}</p>
{% endfor %}
Building on eviltnan's answer, his filter will raise an exception if key isn't a key of dict.
Filters should never raise exceptions, but should fail gracefully. This is a more robust/complete answer:
#register.filter
def keyvalue(dict, key):
try:
return dict[key]
except KeyError:
return ''
Basically, this would do the same as dict.get(key, '') in Python code, and could also be written that way if you don't want to include the try/except block, although it is more explicit.
There is a very dirty solution:
<div>We need d[{{ k }}]</div>
<div>So here it is:
{% for key, value in d.items %}
{% if k == key %}
{{ value }}
{% endif %}
{% endfor %}
</div>