Mohamed
Mohamed

Reputation: 3600

How to access a dictionary element in a Django template?

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"

Upvotes: 242

Views: 293200

Answers (10)

juanignaciosl
juanignaciosl

Reputation: 3573

I'd suggest to add some simple preprocessing in the view, then passing the data to the view. I'd say that it keeps the template simple and it grows nicely as your view become more complicated.

You could create a dataclass containing all the data that you're showing:

@dataclasses.dataclass(frozen=True)
class ChoicesInfo:
    choice: Choice
    votes: int

Then, in the view, you populate the list:

choices_info = [ChoiceInfo(choice, votes[choice.id] for choice in choices]

Then, you pass choices_info instead.

Upvotes: 0

Aaron
Aaron

Reputation: 3399

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.

Upvotes: -4

Nick Legend
Nick Legend

Reputation: 1038

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

Upvotes: 4

John Ewart
John Ewart

Reputation: 1270

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.

Upvotes: 69

russian_spy
russian_spy

Reputation: 6655

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.

Upvotes: 360

Ned Batchelder
Ned Batchelder

Reputation: 375484

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 %}

Upvotes: 42

Adam Starrh
Adam Starrh

Reputation: 6958

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.

Upvotes: 8

Nick Korolkov
Nick Korolkov

Reputation: 443

django_template_filter filter name get_value_from_dict

{{ your_dict|get_value_from_dict:your_key }}

Upvotes: 11

Wilhelm
Wilhelm

Reputation: 6676

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.

Upvotes: 239

Jeff Ober
Jeff Ober

Reputation: 5027

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.

Upvotes: 3

Related Questions