212

I would like to print out the number of votes that each choice got. I have this code in a template:

{% for choice in choices %}
    {{choice.choice}} - {{votes[choice.id]}} <br />
{% endfor %}

votes is just a dictionary while choices is a model object.

It raises an exception with this message:

"Could not parse the remainder"
daaawx
  • 2,798
  • 2
  • 14
  • 15
Mohamed
  • 3,262
  • 7
  • 27
  • 31

9 Answers9

330
choices = {'key1':'val1', 'key2':'val2'}

Here's the template:

<ul>
{% for key, value in choices.items %} 
  <li>{{key}} - {{value}}</li>
{% endfor %}
</ul>

Basically, .items is a Django keyword that splits a dictionary into a list of (key, value) pairs, much like the Python method .items(). This enables iteration over a dictionary in a Django template.

daaawx
  • 2,798
  • 2
  • 14
  • 15
russian_spy
  • 6,014
  • 4
  • 28
  • 26
  • @anacarolinats (and others) just make sure you iterate over both the key,value for choices.items. It should still work. – OldTinfoil Jan 04 '13 at 11:52
  • so in the template engine can't use ( ). BTW Thanks wotks for me. – BlaShadow Nov 04 '13 at 18:40
  • 7
    Nice concise solution to the question. To clarify, `items` is a Python method call on the dictionary, not a Django keyword. As [Alex Martelli points out](http://stackoverflow.com/a/3744713/1174169) it's basically the same as `iteritems`. As Wilhelm answered, the dictionary lookup is 3rd in precedence for dot lookups. If you have an item in your dictionary named `'items'`, you'll get that value back instead of a list of tuples. To test: add `{'items':'oops'}` to your dictionary and you'll get a bulleted list of letters from the word 'oops' – cod3monk3y Nov 08 '13 at 01:53
  • 1
    Use collections.OrderedDict to control the order of the iteration – dnalow Jan 07 '15 at 11:35
  • This should be the accepted answer. Simple and straightforward! – Nikolai Tschacher Oct 28 '15 at 13:16
211

you can use the dot notation:

Dot lookups can be summarized like this: when the template system encounters a dot in a variable name, it tries the following lookups, in this order:

  • Dictionary lookup (e.g., foo["bar"])
  • Attribute lookup (e.g., foo.bar)
  • Method call (e.g., foo.bar())
  • List-index lookup (e.g., foo[2])

The system uses the first lookup type that works. It’s short-circuit logic.

Wilhelm
  • 6,276
  • 5
  • 29
  • 25
  • 48
    In his case choice is a variable. Doing .choice will look up the value for the key "choice" rather the value for the key choice. – ibz Jan 30 '11 at 09:35
  • 1
    +1 for the info, even though the question was kind of a "guess what I'm thinking" question. Thanks Wilhelm. – eficker Oct 25 '11 at 01:31
  • 3
    This even works with nested dictionaries. Python code: `my_dict[1][2]` Template code: `my_dict.1.2` – djsmith Jan 26 '12 at 20:39
  • What if the dictionary of lists, d = {key: [value1,value2]}. I'm not being able to use d.key1 to retrieve the list. Any idea why? – Jorge Leitao Nov 26 '12 at 08:04
  • 2
    @J.C.Leitão Because the correct version is `d.key.1` - note the second `.` – Izkata Apr 19 '13 at 19:42
  • 3
    Check the docs on this though... from "1.6 https://docs.djangoproject.com/en/1.6/topics/templates/#variables" : Note that “bar” in a template expression like {{ foo.bar }} will be interpreted as a literal string and not using the value of the variable “bar”, if one exists in the template context. – jamesc Dec 13 '13 at 18:00
67

To echo / extend upon Jeff's comment, what I think you should aim for is simply a property in your Choice class that calculates the number of votes associated with that object:

class Choice(models.Model):
    text = models.CharField(max_length=200)

    def calculateVotes(self):
        return Vote.objects.filter(choice=self).count()

    votes = property(calculateVotes)

And then in your template, you can do:

{% for choice in choices %}
    {{choice.choice}} - {{choice.votes}} <br />
{% endfor %}

The template tag, is IMHO a bit overkill for this solution, but it's not a terrible solution either. The goal of templates in Django is to insulate you from code in your templates and vice-versa.

I'd try the above method and see what SQL the ORM generates as I'm not sure off the top of my head if it will pre-cache the properties and just create a subselect for the property or if it will iteratively / on-demand run the query to calculate vote count. But if it generates atrocious queries, you could always populate the property in your view with data you've collected yourself.

Vishal Singh
  • 5,673
  • 2
  • 16
  • 30
John Ewart
  • 1,240
  • 1
  • 9
  • 6
  • thanks @john ewart, your solution worked for me. I am newbie to django and python and can't figureout how to get the sql that ORM generated. – Mohamed Aug 14 '09 at 08:27
  • You can find the answer to that bit over here: http://docs.djangoproject.com/en/dev/faq/models/#how-can-i-see-the-raw-sql-queries-django-is-running It's quite simple, actually and can be displayed in your template, or logged with a logging facility, but you have to remember to turn DEBUG on for this to work. – John Ewart Aug 14 '09 at 14:04
  • this solution is perfect for a problem I've been having with django templating + google app engine models. I wish I could vote you up twice. – Conrad.Dean May 07 '11 at 16:32
  • 6
    While it does work, it's not very efficient. It is doing sql queries in a loop (something you should avoid). Creating your own tag to do dict lookups is easy: @register.filter def lookup(d, key): if d and isinstance(d, dict): return d.get(key) – dalore Oct 30 '12 at 14:10
  • Creating a class is way too much overhead; a better structured dictionary, combined with the `.items` call (as illustrated in one of the other answers) is a far simpler solution. – Zags Feb 07 '14 at 23:08
  • Why choice.choice? Choice.Text not? – Timo Aug 02 '18 at 09:05
35

You need to find (or define) a 'get' template tag, for example, here.

The tag definition:

@register.filter
def hash(h, key):
    return h[key]

And it’s used like:

{% for o in objects %}
  <li>{{ dictionary|hash:o.id }}</li>
{% endfor %}
Ned Batchelder
  • 345,440
  • 70
  • 544
  • 649
8

django_template_filter filter name get_value_from_dict

{{ your_dict|get_value_from_dict:your_key }}
Nick Korolkov
  • 393
  • 5
  • 4
8

Similar to the answer by @russian_spy :

<ul>
{% for choice in choices.items %} 
  <li>{{choice.0}} - {{choice.1}}</li>
{% endfor %}
</ul>

This might be suitable for breaking down more complex dictionaries.

Adam Starrh
  • 5,698
  • 7
  • 46
  • 82
3

Ideally, you would create a method on the choice object that found itself in votes, or create a relationship between the models. A template tag that performed the dictionary lookup would work, too.

Jeff Ober
  • 4,857
  • 19
  • 15
2

Could find nothing simpler and better than this solution. Also see the doc.

@register.filter
def dictitem(dictionary, key):
    return dictionary.get(key)

But there's a problem (also discussed here) that the returned item is an object and I need to reference a field of this object. Expressions like {{ (schema_dict|dictitem:schema_code).name }} are not supported, so the only solution I found was:

{% with schema=schema_dict|dictitem:schema_code %}
    <p>Selected schema: {{ schema.name }}</p>
{% endwith %}

UPDATE:

@register.filter
def member(obj, name):
    return getattr(obj, name, None)

So no need for a with tag:

{{ schema_dict|dictitem:schema_code|member:'name' }}
Nick Legend
  • 584
  • 1
  • 4
  • 16
-3

You could use a namedtuple instead of a dict. This is a shorthand for using a data class. Instead of

person = {'name':  'John', 'age':  14}

...do:

from collections import namedtuple
Person = namedtuple('person', ['name', 'age'])
p = Person(name='John', age=14)
p.name # 'John'

This is the same as writing a class that just holds data. In general I would avoid using dicts in django templates because they are awkward.

Aaron
  • 3,051
  • 3
  • 30
  • 48