reedvoid
reedvoid

Reputation: 1253

Django template lists not iterable?

Running into a strange problem with Django's template, and being n00b I don't even know how to debug a template...

PROBLEM: variables of a type list somehow stopped being a list when passed into the template.

In my view, I have a bunch of variables passed to the template that are a dictionary of lists. Here's the code,

VIEW

project_image_design = {}

for p in projects:    
    project_image_design[p.id] = []    
    images = UploadedImage.objects.filter(project=p, image_type=UploadedImage.DESIGN)

    for i in images:
        project_image_design[p.id].append(i)

Here's the context. I have projects, each contains images. I created a dictionary, where the keys are the project id, and the value is a list of images associated with that project.

However, when I use this in the template, things go wrong,

TEMPLATE

{% for p in projects %}
    <div class="row">

        {% for list in project_image_design|get_item:p.id %}
            {% for i in list %} 
                <div class="col-md-2"><img src = "{% get_static_prefix %}media/{{ i.filename }}"></div>
            {% endfor %}
        {% endfor %}

    </div>
{% endfor %}

So in the template, I'm iterating through projects, then using the project's id (p.id) to get the dictionary value, which is a list of images, and then iterating through that. The fancy get_item tag is just a way to access dictionary values through keys that not straight-forward variables (see: Django template how to look up a dictionary value with a variable).

Anyway, I get this error:

TypeError at /designer/my_projects/
'UploadedImage' object is not iterable

The error occurs on this line: {% for i in list %}, which is the line where I'm iterating through the list of images I retrieved using my project's id.

What's going on here????

I double-checked via pdb in the view, it all checks out. The variable being passed is indeed a dictionary of lists, I put a type on all the individual dictionary elements and they're all lists (like: type(project_image_design[1]) would return <class 'list'>).

Also, at the moment, all the lists are of length 1. I'm wondering maybe the template sort of deflates lists that are size 1? That'd seem like a pretty weird thing to do, probably not the reason.

Any help would be appreciated.

Also, how do I debug templates the way I can debug Python code? Like stepping through and stuff? Is that even possible?

Upvotes: 0

Views: 742

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 599778

It hasn't stopped being a list. But you have two nested for loops: you iterate through the items you get from the dictionary - confusingly calling each item list - and then attempt to iterate again through items in that "list". But the inner loop makes no sense: you should be doing simply:

{% for i in project_image_design|get_item:p.id %}
    <div class="col-md-2"><img src = "{% get_static_prefix %}media/{{ i.filename }}"></div>
{% endfor %}

I'd also point out that your view logic is over-complicated. It could be reduced to simply this:

for p in projects:    
    project_image_design[p.id] = UploadedImage.objects.filter(project=p, image_type=UploadedImage.DESIGN)

And in fact it could be simplified even further: you don't need the dictionary, or the get_item tag, at all. Instead, provide a method on Project called something like design_images which just returns the images of that type:

def design_images(self):
    return self.uploadedimage_set.filter(image_type=UploadedImage.DESIGN)

and removing the dictionary logic from the view altogether, and now your template can just be:

{% for p in projects %}
    <div class="row">

        {% for i in p.design_images %}
            <div class="col-md-2"><img src = "{% get_static_prefix %}media/{{ i.filename }}"></div>
        {% endfor %}
    </div>
{% endfor %}

Upvotes: 3

Related Questions