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?
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 %}
Related
So here is the set up (simplified for here):
In my template file I have two sections I would like to add content too by looping of a list of dictionaries, e.g.:
<div class="stuff-a"></div>
<div class="stuff-b"></div>
{% for thing in list %}
<!-- goes into div a -->
<!-- goes into div b -->
{% endfor %}
I thought this would work:
<div class="stuff-a">
{% block diva %}{% endblock %}
</div>
<div class="stuff-b">
{% block divb %}{% endblock %}
</div>
{% for thing in list %}
{% block diva %} thing.stuff_for_div_a {% endblock %}
{% block divb %} thing.stuff_for_div_b {% endblock %}
{% endfor %}
and then django complains about me using each block more than once - shame on me.
Now the solution to this (keeping the same schema) is to make an intermediate template file and then extend it.
My question is, is there a way to append to different specific areas in a django template without having to loop through a list twice or an intermediate template file?
This matters if the loop is somewhat complicated and you dont want to have to update two loops whenever you make a change.
When might this occur in practice (outside this example)? If you have a list where each element in a list references another list (e.g. the way bootstrap handles tabnation)
Now the solution to this (keeping the same schema) is to make an intermediate template file and then extend it.
You can't do this either because you can't use {% block %} in a loop for the same reason as you can't have a block more than once in the same template.
My question is, is there a way to append to different specific areas in a django template without having to loop through a list twice or an intermediate template file?
No you can't. And if ever you could do what you are trying to achieve, it would necessarily involve looping twice over the list behind the scenes.
This matters if the loop is somewhat complicated and you dont want to have to update two loops whenever you make a change.
It matters indeed if the loop is a generator where the next element is calculated at each iteration. But in this case you wouldn't be able to loop twice over the iterable anyway, which is necessary.
If it is a list already calculated and stored in memory, it doesn't matter.
When might this occur in practice (outside this example)? If you have a list where each element in a list references another list (e.g. the way bootstrap handles tabnation)
I don't see how this would be complicated.
I am experimenting with using Grav to create my next website. One of the things I would be able to do is to build a unordered list using data provided in Grav frontmatter from the Grav page that uses the template. Here is how I am trying to do this
---
sectionone:
listitems: "['Benefit 1','Benefit 2','Benefit 3']"
---
and then in the template somehow do the following
{% block featurelist %}
<ul>
{% set items = {{page.header.sectionone.consumers.benefits|json_decode}} %}
{% for item in {{}} %}
<li>{{item}}</li>
{% endfor %}
</ul>
{% endblock %}
However, Twig does not like this and reports back an error along the lines of
Twig_Error_Syntax
A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "punctuation" of value "{".
with the offending line being my {% set items = ... } statement. I am clearly doing something wrong here but I am a Twig newbie so I fail to see what that might be.
{% block featurelist %}
<ul>
{% set items = page.header.sectionone.consumers.benefits|json_decode %}
{% for item in items %}
<li>{{item}}</li>
{% endfor %}
</ul>
{% endblock %}
I figured this out eventually. Grav documentation is by and large very good. However, the documentation on page headers/frontmatter appears to be somewhat incomplete. There is no full description of the entire syntax that is understood by the frontmatter processor. In order to define an array in front matter all you need to do is the following
---
sectionone:
benefits:
- 'Benefit 1'
- 'Benefit 2'
- ...
---
In essence the standard markdown syntax for an unordered list. Grav's twig processor appears to convert this to a PHP array - no parsing required!
I need compare values of a dict items inside a loop for in a django template.
{% for room in hotel.RoomRS %}
<p class="precio-old">1000,99€</p>
<p class="precio-new">{{ room.RoomRates.TotalAmount }}</p>
<p class="noche">120€ / noche</p>
{% endfor %}
This code return some integers values. I need compare it and choose the smallest one
Never put too many logic to Django templates. Instead, do the following in your view and then pass it to context. To get minimum dict value in Python, do:
min(my_dict, key=my_dict.get)
I am generating some Django template code on the fly, in order to display rows in tables that are not stored in
a Django database and do not have models. I know the database and I can introspect them if needed, but I don't want
to write code by hand.
For example, field PSOPRDEFN.OPRCLASS stores an optional reference to a particular row where PSCLASSDEFN.OPRID=PSOPRDEFN.OPRCLASS, essentially a foreign key relationship. If there is no relationship PSOPRDEFN.OPRCLASS has one ' ' (space character) in it.
I also have a page for a given PSCLASSDEFN row, where the url is:
url(r'^(?i)permissions/(?P<CLASSID>[A-Z0-9_&]{1,50})/$',
'pssecurity.views.psclassdefn_detail',
name="psclassdefn_detail"),
Note that the ?P CLASSID regular expression does not allow for blanks which corresponds to gets stored in the PSCLASSDEFN table - I figure it's safer to limit what the user can put in the url request.
Back to my generated template: I want to hyperlink to the relation, if it exists. I feed my home-grown template generator a json "directive" indicating what I want put into the template (thanks for the inspiration, django-tables2):
....
{
"colname": "LANGUAGE_CD"
},
{
"urlname": "security:psclassdefn_detail",
"colname": "OPRCLASS",
"kwargs": [
{
"colname": "dbr",
"accessor": "dbr"
},
{
"colname": "CLASSID",
"accessor": "inst.OPRCLASS"
}
]
},
...
Some fairly trivial code generation then results in:
<div class="row">
<div class="col-xs-6 fieldlabel" title="LANGUAGE_CD" >Language Code</div>
<div class="col-xs-6 fieldvalue text-left _fv_LANGUAGE_CD">{{inst.LANGUAGE_CD}}</div>
</div>
<div class="row">
<div class="col-xs-6 fieldlabel" title="OPRCLASS" >Primary Permission List</div>
<div class="col-xs-6 fieldvalue _fv_OPRCLASS">
{% if inst.OPRCLASS|slugify %}
{{inst.OPRCLASS}}
{% endif %}
</div>
</div>
My problem is that started getting random Template url resolution errors when displaying some of the PSOPRDEFN data. I eventually tracked it down to the blank OPRCLASS fields in some rows.
In order to avoid this I first added
{% if inst.OPRCLASS %}
<a ...></a>
{% endif %}
That didn't work because the field is not empty, it is blank (and therefore doesn't match the CLASSID regex). So, this is where I read the filter docs again and found that slugify strips out blanks and non-alpha.
{% if inst.OPRCLASS | slugify %}
<a ...></a>
{% endif %}
Works, as a workaround. The problem is that CLASSID only stores alphanum, but that's not always true for other fields. I wouldn't mind introspecting the table column definition at template generation runtime to see what to do, but I need to find an appropriate way to disable url reversal, for only some rows.
Questions. Is there a better filter, such as a |strip? I suppose I could always build my own filter.
Even better, is there a tag to selectively catch NoReverseMatch' exceptions at template generation time?
{% try NoReverseMatch %}
{{inst.OPRCLASS}}
{% endtry %}
The reason I was so verbose in my description is because this is not something that can be worked around using Models. And neither can I custom-tune the template by hand. I find Django works quite well without models in most cases, but url reversing in templates can be quite brittle when a few rows of data do not match expectations. Hardening it would be very beneficial.
You can assign the result of the url tag to a variable.
{% url 'path.to.view' arg arg2 as the_url %}
{% if the_url %}
link
{% else %}
No link
{% endif %}
This syntax does not raise an exception if reversing the view fails.
Overlap in terminology makes search for answers difficult for this one.
I'm looking for advice on the best way to implement a multiple-column display of my QuerySet that fills each column top to bottom over X columns. Meaning that the number of items in each column equals the QuerySet count divided by X (number of columns).
Using Offset doesn't work for this because I would like my data to grow into 4 columns without updating the offset manually. CSS floats work visually, but leave the data out of order.
Something like that should work for you, pass the number of columns as columns to the template:
{% for item in items %}
{% if forloop.first %}<div style="float:left;">{% endif %}
{{ item }}
{% if forloop.counter|divisibleby:columns %}
</div><div style="float:left">
{% endif %}
{% if forloop.last %}</div>{% endif %}
{% endfor %}
<div style="clear:both;"></div>
It seems like lazerscience's answer is on the right track - but I think the OP wants the data to alphabetically sort the data down the column and then start back at the top of the next column. Using divisibleby: works to break the line after 'X' items, but I think it'll take a more complex template to do what the OP wants.
Not sure if it's possible, but in the view could you: Get item count, divide by 'columns' and used that # in Divisibleby to break into the next DIV column (the visual flow will be CSS)
As Lazers's example is now you're constructing Rows and then breaking it into columns, leaving the sort order across and then down.
Sorry if I missed something.
-K
You'd better go and use a jQuery plugin to make some columns from a list.
Columnizer works very well for me
Here are some django template filter that split a list into multiple sub-lists:
list partition template filters at djangosnippets.org
You could use these in a django template to split a long list into multiple columns as follows:
{% load list_tags %}
<h2>Some List</h2>
{% for sub_list in full_list|rows:"3" %}
<ul>
{% for item in sub_list %}
<li>
{{item.name}}
</li>
{% endfor %}
</ul>
{% endfor %}
I included the template filters in my project in a file called list_tags.py. Adjust the {% load %} tag as necessary.