2008-12-17

Missteps in Django - Part 1

Welcome back fellow Avocadoists! I finally got something up and running in Django and I'm going to tell you about some problems I had learning and using Django in the process. I'm working on my own as the programmer and the designer so I'm sure that colors my view of things.

My first issue was with in Django's templates. Namely, there seems to be no way to look things up dynamically in a dictionary based on the value of another template tag. I'm not even sure that makes sense so here's an example of what I was trying to do:
# the python part in your view method
things_by_day = {
'2008-11-28' : [thing1, thing2],
'2008-11-29' : [thing3, thing4, thing 5],
'2008-11-30' : [thing6],
}
days = d.keys()
days.sort()

# The Django template part
{% for day in days %}
Here are the things for {{ day }}
{% for thing in things_by_day.day %}
do the thing with the list of things
{% endfor %}
{% endfor %}

So the part in the template where it says "for thing in things_by_day.day" is the part that doesn't work the way my brain expected it to. I expected the value of "things_by_date.day" to be the Python equivalent of "things_by_day[day]" but instead Django's template language translates this as "things_by_day['day']". It treats the string "day" as the key. Fail.

I figured out there was a way to do almost what I wanted using Django's regroup tag, but "No sir! I don't like it!"

Here's what the template part looks like using regroup. Note that in the following template "things" is a queryset on a model with a field named "created" instead of a simple dict or list.


{% regroup things by created as things_by_date %}
{% for thing_date in things_by_date %}
Here are the things for {{ thing_date.grouper }}
{% for thing in thing_date.list %}
do the thing with the list of things
{% endfor %}
{% endfor %}


I find this syntax noxious and I can't remember what name to use where. In the process of writing this, I got it wrong in nearly every possible way. It's probably still wrong (I'm sure you'll let me know). What's with the magic names like "grouper" and "list"? I really just want to let Python be Python and use a dictionary in a normal way. I understand the limitations of Django's template system and why they were made and for the most part I support those decisions. Then the ice weasels come.

I'm sure someone has made a new template tag to do dict lookups the way I was trying to do. It may be this one. Please feel free to let me know in the comments. I'm still quite new to Django and eager to learn more. Thanks!

6 comments:

andy said...

Are there external reasons that you have to pass that particular data structure into the template because I can think of data structures that would make it much easier to do what you want. (a list of (day,item-list) tuples for instance?

Brandon Craig Rhodes said...

I agree with Andy. This is what I have always done:


days = d.items()
days.sort()

# The Django template part
{% for day, things in days %}
Here are the things for {{ day }}
{% for thing in things %}
do the thing with the list of things
{% endfor %}
{% endfor %}

Pythonic Avocado said...

Thanks Andy and Brandon.

There wasn't any good reason to use a dictionary here, but my brain wanted a dictionary so that's where I started. Then I found regroup and went with it. Given how much I seem to dislike regroup, I should probably change my code to get rid of it and use a list of tuples. It seems like the obvious winner.

Ciao.

John Bruce said...

I ran into this exact same problem today and found something that works great for me. I actually created my own filter that acts like the get() method of a dictionary:

@register.filter
def lookup(value, arg):
return value.get(arg)

lookup.is_safe = True

You'll have to install it using the following steps:
1. Create a directory called "templatetags" in your django application directory.
2. create / touch a blank file named __init__.py in your newly created directory.
3. Create a file (this will be the name of the package you load using the: {% load MODULE_NAME %} syntax in your template) with the extension py.
4. Paste the above code into the file, load the module, and you can then use the template like this:

{% for key,value in your_dict.items %}
{{ your_dict|lookup:key }}
{% endfor %}

same as:

for key,value in your_dict:
print your_dict[key]
print your_dict.get(key)

Hopefully this proves helpful!

John B.

Pythonic Avocado said...

Hi John B.,

Nice! Thank you! This should be on the Django snippets to make it easier to find.

alastairc said...

It's not an answer in anyway, but perhaps an insight...

One of the principles behind Django templates is that they can be used by non-programmers (i.e. people who generally use HTML/CSS doing front-end development). They are also specifically aimed to not be Python, and only use basic constructs.

As a non-programmer, I really struggled to understand what you were trying to do and what the problem was!

So in that regard, I think it's partially a matter of target audience: Django templates work very well for me and the front-end devs at my company.