Adding defaults to column headers in django - django

I have a script which imports data in tables from other websites. The tables are between 5 and 15 columns wide and arbitrarily long.
After I've got the raw data I want the opportunity to make sure my guesses about the column headers are correct. So I want to have a at the top with a list of the 15 things a column could be called. That way I can quickly correct any poor decisions made by my automatic code.
So the auto code generates 2 arrays, the first of strings:
possible_headers = ["one", "two", "three"...]
The second of indexes into that first array
likely_headers = [2, 0, 5...]
(the columns headers would be "three" then "one" then "six")
And use them like this in my template:
{% for likely_head in likely_headers %}
<th>
<select name="colHeader">
{% for poss_head in possible_headers %}
{% if forloop.counter0 == likely_headers.forloop.parentloop.counter0 %}
<option value="Col:{{forloop.counter0}}" selected>{{poss_head}}</option>
{% else %}
<option value="Col:{{forloop.counter0}}">{{poss_head}}</option>
{% endif %}
{% endfor %}
</select>
</th>
{% endfor %}
With the idea that the likely header will be the selected/default item in the select input. Problem is the:
likely_headers.forloop.parentloop.counter0
Doesn't evaluate. forloop.parentloop.counter0 works correctly but apparently it cannot be used as an index into a list.
I'm new to django so I'm probably doing it all wrong, please be nice!

I don't see why you're using likely_headers.forloop.parentloop.counter0 when you should use forloop.parentloop.counter0 according to docs https://docs.djangoproject.com/en/dev/ref/templates/builtins/#for
You may also try solving it by encapsulating counter with {% with %} tag
{% for likely_head in likely_headers %}
{% with forloop.counter0 as parent_counter %}
{% for poss_head in possible_headers %}
{{ parent_counter }}
{% endfor %}
{% endwith %}
{% endfor %}
I haven't checked that it works for sure but I think it should.
Also you're probably trying to solve problem that should not be solved in templates. You can try using tags, processing headers in view (using library?) and just returning list of headers that should be rendered.

The list of integers I thought I had was in truth a list of strings ["1", "5", "3"] so they failed comparison to the for loop iterator. Creating a real list of ints solved the problem.

Related

Double loop with django Templates

so I have this matrix sent to a view
[[6.197, 6.156, 6.165, 6.164, 4.741], [6.191, 6.106, 6.175, 6.132, 4.741], [6.158, 6.137, 6.137, 6.133, 4.741]]
and a list containing dates
["11-12-2016","12-12-2016","13-12-2016"]
and I want to format them with Template to look like this
[["11-12-2016",6.197, 6.156, 6.165, 6.164, 4.741]
["12-12-2016",6.191, 6.106, 6.175, 6.132, 4.741]
....]
Iam using this code :
{% for date in dates %}
{% with forloop.counter0 as i %}
,["{{date}}"{% for item in selling.i %} ,{{item}} {% endfor %}]
{% endwith %}
{% endfor %}
and it doesn't work , but when I replace i with 0,1.. the second loop works fine on one list
{% for item in selling.i %}
This isn't going to work -- the template will look for an attribute or index literally equal to "i", and not the value of that variable.
The Django template language actively discourages using too much logic in the template, and this is an example of something you can't do.
So create the lists as you want them in Python, and pass those to the template. E.g. in Python
combined = [[str(date)] + sell for date, sell in zip(dates, selling)]
And in the template
{% for row in combined %}
[{{ row|join:","|safe }}],
{% endfor %}

Accessing nested list by variable in Django templates [duplicate]

I have some loop on the page and need list item depending from loop number.
When I call:
{{ mylist.1 }}
{{ mylist.2 }}
{{ mylist.3 }}
all works fine but what I really need is something like:
{% for x in somenumber|MyCustomRangeTag %}
{{ mylist.x }}
{% endfor %}
MyCustomRangeTag gives me Python range() it works and I already have x as number. So x is 1, 2, 3 etc. depending from loop number.
Is this possible and how?
This is not possible directly because Django thinks that "x" is the key to lookup in mylist - instead of the value of x. So, when x = 5, Django tries to look up mylist["x"] instead of mylist[5].
Use the following filter as workaround:
#register.filter
def lookup(d, key):
return d[key]
and use it like
{{ mylist|lookup:x }}
The slice tag in Django templates may use python's slicing code, but the syntax is unmistakably different. For instance, if you wanted to get an element of a sequence with a variable, in python you'd write something similar to the following:
>>>mylist = ["0th Element", "1th Element"]
>>>zero, one = 0, 1
>>>mylist[zero]
"0th Element"
>>>mylist[one]
"1th Element"
Using this syntax with the Django slice template tag will return a sliced list in every case, of dubious utility for getting an item of known index:
{% with "0" as zero %}
{% with "1" as one %}
{% with "2" as two %}
{{mylist|slice:zero}} {{mylist|slice:one}} {{mylist|slice:two}}
{% endwith %}
{% endwith %}
{% endwith %}
Renders to the html:
[] ["0th Element"] ["0th Element", "1th Element"]
Note the differences: you are getting the result of mylist[:x] instead of mylist[x].
Django provides enough tools to work around this. The first trick is to use explicit slices like 0:1 for your indices, and then |join:"" the resultant list into a single element. Like so:
{% with "0:1" as zero %}
{{mylist|slice:zero|join:""}}
{% endwith %}
Yields:
0th Element
This comes in particularly handy if you need to access a parent loop's index when dealing with an iterable inside a child loop:
{% for parent in parent_loop %}
{% cycle "0:1" "1:2" "2:3" as parent_loop_index silent %}
{% for child in child_loop %}
{{child|slice:parent_loop_index|join:""}}
{% endfor %}
{% endfor %}
Completed with nothing but stock parts, although I don't think Django has implemented achievements yet.
I notice that #e-satis mentioned it, but I think the built-in slice template tag deserves some love.
{{ item | slice:"2" }} #gets the third element of the list
Are you sure you can't just do:
{% for item in mylist %}
{{ item }}
{% endfor %}
With the slice filter, you can even do some customisation.
Following worked for me
{% for 1,2,3 in mylist %}
# do stuff
Just don't use brackets around 1,2,3

Get list item dynamically in django templates

I have some loop on the page and need list item depending from loop number.
When I call:
{{ mylist.1 }}
{{ mylist.2 }}
{{ mylist.3 }}
all works fine but what I really need is something like:
{% for x in somenumber|MyCustomRangeTag %}
{{ mylist.x }}
{% endfor %}
MyCustomRangeTag gives me Python range() it works and I already have x as number. So x is 1, 2, 3 etc. depending from loop number.
Is this possible and how?
This is not possible directly because Django thinks that "x" is the key to lookup in mylist - instead of the value of x. So, when x = 5, Django tries to look up mylist["x"] instead of mylist[5].
Use the following filter as workaround:
#register.filter
def lookup(d, key):
return d[key]
and use it like
{{ mylist|lookup:x }}
The slice tag in Django templates may use python's slicing code, but the syntax is unmistakably different. For instance, if you wanted to get an element of a sequence with a variable, in python you'd write something similar to the following:
>>>mylist = ["0th Element", "1th Element"]
>>>zero, one = 0, 1
>>>mylist[zero]
"0th Element"
>>>mylist[one]
"1th Element"
Using this syntax with the Django slice template tag will return a sliced list in every case, of dubious utility for getting an item of known index:
{% with "0" as zero %}
{% with "1" as one %}
{% with "2" as two %}
{{mylist|slice:zero}} {{mylist|slice:one}} {{mylist|slice:two}}
{% endwith %}
{% endwith %}
{% endwith %}
Renders to the html:
[] ["0th Element"] ["0th Element", "1th Element"]
Note the differences: you are getting the result of mylist[:x] instead of mylist[x].
Django provides enough tools to work around this. The first trick is to use explicit slices like 0:1 for your indices, and then |join:"" the resultant list into a single element. Like so:
{% with "0:1" as zero %}
{{mylist|slice:zero|join:""}}
{% endwith %}
Yields:
0th Element
This comes in particularly handy if you need to access a parent loop's index when dealing with an iterable inside a child loop:
{% for parent in parent_loop %}
{% cycle "0:1" "1:2" "2:3" as parent_loop_index silent %}
{% for child in child_loop %}
{{child|slice:parent_loop_index|join:""}}
{% endfor %}
{% endfor %}
Completed with nothing but stock parts, although I don't think Django has implemented achievements yet.
I notice that #e-satis mentioned it, but I think the built-in slice template tag deserves some love.
{{ item | slice:"2" }} #gets the third element of the list
Are you sure you can't just do:
{% for item in mylist %}
{{ item }}
{% endfor %}
With the slice filter, you can even do some customisation.
Following worked for me
{% for 1,2,3 in mylist %}
# do stuff
Just don't use brackets around 1,2,3

Building a list in Django templates

With this code:
{% for o in [1,2,3] %}
<div class="{% cycle 'row1' 'row2' %}">
{% cycle 'row1' 'row2' %}
</div>
{% endfor %}
I get a TemplateSyntaxError:
Could not parse the remainder: '[1,2,3]' from '[1,2,3]'
Is there a way of building a list in a template?
We can use split method on str object :
page.html :
{% with '1 2 3' as list %}
{% for i in list.split %}
{{ i }}<br>
{% endfor %}
{% endwith %}
Results :
1
2
3
You can do it via cunning use of the make_list filter, but it's probably a bad idea:
{% for o in "123"|make_list %}
<div class="{% cycle 'row1' 'row2' %}">
{% cycle 'row1' 'row2' %}
</div>
{% endfor %}
p.s. You don't seem to be using o anywhere, so I'm not sure what you're trying to do.
I made this template tag to achieve this goal.
from django import template
register = template.Library()
# use #register.assignment_tag
# only when you're working with django version lower than 1.9
#register.simple_tag
def to_list(*args):
return args
to use it in template:
{% load your_template_tag_file %}
{% to_list 1 2 3 4 5 "yes" as my_list %}
{% for i in my_list %}
{{ i }}
{% endfor %}
Reference here:
Django simple tags
The other answers here look like the ticket (at least for what I wanted), so I'll provide an answer as to WHY you might want to do something like this (and perhaps there's a better answer for my case than what's been provided):
I came across this question looking for a way to build 3 very similar, but not identical buttons using Bootstrap. One button might look like
<div class="btn-group">
<a class="btn btn-primary dropdown-toggle" data-toggle="dropdown" href="#">
Modality
<span class="caret"></span>
</a>
<ul class="dropdown-menu" id="Modality">
<li>Action</li>
</ul>
</div>
where the difference between buttons is limited to the text of the button (Modality, on its own line above) and the contents of the pertaining to the button, which we'll assume is filled dynamically by JS (referencing id="Modality").
If I need to make 10 of these, copy/pasting the HTML seems dumb and tedious, especially if I want to change anything about my button after the fact (like making all of them split-drop-downs) and it goes against DRY.
So, instead, in the template I could do something like
{% with 'Modality Otherbutton Thirdbutton' as list %}
{% for i in list.split %}
<!-- copy/paste above code with Modality replaced by {{ i }} -->
{% endfor %}
{% endwith %}
Now, granted, in this particular case the buttons add functionality to some related data grid, so the button names could be dynamically filled from django model-sourced data as well, but I'm not at that stage in my design right now, and you can see where this sort of functionality is desirable to maintain DRY.
The simplest is to do
{% for x in "123" %}
drodger is correct, you can't do that in the deliberately-crippled Django template lanuage. Either pass in the list as a context variable when you invoke the template or try a template tag like expr. Then you can say {% expr [1,2,3] as my_list %} and then use my_list in your for loop.
This maybe an inspiration. Use the buildin filter add.
{{ first|add:second }}
first is [1, 2, 3] and second is [4, 5, 6], then the output will be [1, 2, 3, 4, 5, 6].
This filter will first try to coerce both values to integers.
If this fails, it'll attempt to add the values together anyway.
This will work on some data types (strings, list, etc.) and fail on others.
If it fails, the result will be an empty string.
The official specification,https://docs.djangoproject.com/zh-hans/2.0/ref/templates/builtins/#built-in-filter-reference

Alternate Row Coloring in Django Template with More Than One Set of Rows

Django templates offer the builtin tag cycle for alternating between several values at different points in a template (or for loop in a template) but this tag does not reset when it is accessed in a scope outside of the cycles definition. I.e., if you have two or more lists in your template, the rows of all of which you'd like to use some css definitions odd and even, the first row of a list will pick up where the last left off, not with a fresh iteration from the choices (odd and even)
E.g., in the following code, if the first blog has an odd number of entries, then the first entry in a second blog will start as even, when I want it to start at odd.
{% for blog in blogs %}
{% for entry in blog.entries %}
<div class="{% cycle 'odd' 'even' %}" id="{{entry.id}}">
{{entry.text}}
</div>
{% endfor %}
{% endfor %}
I've tried obviating this by patching with the resetcycle tag offered here:
Django ticket: Cycle tag should reset after it steps out of scope
to no avail. (The code didn't work for me.)
I've also tried moving my inner loop into a custom tag, but this also did not work, perhaps because the compile/render cycle moves the loop back into the outer loop? (Regardless of why, it didn't work for me.)
How can I accomplish this simple task!? I'd prefer not to create a data structure in my view with this information pre-compiled; that seems unnecessary. Thanks in advance.
The easiest workaround (until the resetcycle patch gets fixed up and applied) is to use the built-in "divisibleby" filter with forloop.counter:
{% for entry in blog.entries %}
<div class="{% if forloop.counter|divisibleby:2 %}even{% else %}odd{% endif %}" id="{{ entry.id }}">
{{ entry.text }}
</div>
{% endfor %}
A little more verbose, but not hard to understand and it works great.
https://docs.djangoproject.com/en/1.8/ref/templates/builtins/#cycle
{% for o in some_list %}
<tr class="{% cycle 'row1' 'row2' %}">
...
</tr>
{% endfor %}
Give up and use Jinja2 Template System
I gave up on django template language, it's very restricted in what you can do with it.
Jinja2 uses the same syntax that the django template uses, but adds many enhancements over it.
EDIT/NOTE ( I know it sounds like a big switch for just a minor issue, but in reality I bet you always find yourself fighting the default template system in django, so it really is worthwhile and I believe it will make you more productive in the long run. )
You can read this article written by its author, although it's technical, he mentions the problem of the {% cycle %} tag in django.
Jinja doesn't have a cycle tag, it has a cycle method on the loop:
{% for user in users %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li>
{% endfor %}
A major advantage of Jinja2 is that it allows you to use logic for the presentation, so if you have a list of pictures, you can put them in a table, because you can start a new row inside a table every N elements, see, you can do for example:
{% if loop.index is divisibleby(5) %}
</tr>
{% if not loop.last %}
<tr>
{% endif %}
{% endif %}
you can also use mathematical expressions:
{% if x > 10 %}
and you can access your python functions directly (but some setup is required to specify which functions should be exposed for the template)
{% for item in normal_python_function_that_returns_a_query_or_a_list() %}
even set variables ..
{% set variable_name = function_that_returns_an_object_or_something() %}
You can use tagged cycle and resetcycle (new in Django 1.11) calls (from https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#std:templatetag-resetcycle ):
{% for blog in blogs %}
{% cycle 'odd' 'even' as rowcolors silent %}
{% resetcycle rowcolors %}
{% for entry in blog.entries %}
{% cycle rowcolors %}
<div class="{{ rowcolors }}" id="{{entry.id}}">
{{ entry.text }}
</div>
{% endfor %}
{% endfor %}
I end up doing so, with the forloop.counter0 - It works great!
{% for product in products %}
{% if forloop.counter0|divisibleby:4 %}<div class="clear"></div>{% endif %}
<div class="product {% if forloop.counter0|divisibleby:4 %}col{% else %}col20{% endif %}">
Lorem Ipsum is simply dummy text
</div>
{% endfor %}
The easiest answer might be: "give up and use jQuery." If that's acceptable it's probably easier than fighting with Django's templates over something so simple.
There's a way to do it server-side with an iterator that doesn't keep a simultaneous copy of all the entries:
import itertools
return render_to_response('template.html',
{
"flattened_entries": itertools.chain(*(blog.entries for blog in blogs)),
})