Double loop with django Templates - django

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

Related

How to acess form based on index django

I am trying to access the form based on the index value how can i do that exactly?
Ex:
{% for line in data_lines %}
{{line}}
{% with x=forloop.counter %}
{{form.x}}
{% endwith %}
{% endfor %}
As Django discourages adding too much logic hence I don't think it's possible using built-in Django Template features. You can achieve this by sending a dict instead of data_lines and accessing via key, value as:
{% for key, value in data_lines.items %}
{{value}}
{{ form.key }}
{% endfor %}
If you really want it then you could write a custom template filter.

Check if object is in manytomany list in template

How can I check if a certain object/id is in a list?
I want something to be displayed if the ID of the connected object is not "6".
Tried with something like this:
{% if user.benefits.all != "6" %}
You do not have a benefit with ID 6.
{% endif %}
It is better to not put much logic into templates. View (or model) - is a better place for that.
For example in view you can check, that user.benefits has element with id=6 by this code:
has_benefit = user.benefits.filter(id=6).count() > 0
context['has_benefit'] = has_benefit
Now in template just use this new context variable:
{% if not has_benefit %}
You do not have a benefit with ID 6.
{% endif %}
UPDATED:
If you still want to do it in template, it is better to create a custom template filter:
from django import template
register = template.Library()
#register.filter(name='has_benefit')
def has_benefit(user, benefit_id):
b_id = int(benefit_id)
return user.benefits.filter(id=b_id).count() > 0
Now in template load your templatetags module using {% load module_name %} and use:
{% if not user|has_benefit:"6" %}
You do not have a benefit with ID 6.
{% endif %}
{% for benefit in user.benefits.all %}
{% if benefit.id != 6 %}
You do not have a benefit with id 6
{% endif %}
{% endfor %}
But this will loop through all the benefits and print it every time the condition passes.
So, you should write a template tag which returns you a list of ids for all benefits for a particular user and once you have that list you can do:
{% if 6 not in list_of_benefit_ids %}
You do not have a benefit with id 6
{% endif %}

Adding defaults to column headers in 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.

Django template for loop. Member before

I want to create such loop:
{% for object in objects %}
{% if object.before != object %}
{{ object }} this is different
{% else %}
{{ object }} this is the same
{% endfor %}
Based on https://docs.djangoproject.com/en/dev/ref/templates/builtins/?from=olddocs#for I can't. Is there really no simple way to do this? Or I just need to use counter and check for objects[counter-1]?
P.S. .before is theoretical and objects is simple query list. I want to take and do something with the loop member that encountered before current loop member.
Check ifchanged template tag
There is a "simple way" to do this: write a custom template tag. They're really not hard. This would probably do the trick (untested):
#register.simple_tag
def compare_objects(object_list):
comparisons = []
for i in range(1, len(object_list)):
if object_list[i] > object_list[i-1]:
comparisons.append('bigger')
else:
comparisons.append('smaller')
return comparisons
The built-in template tags and filters don't make it easy (as of Django 1.4), but it is possible by using the with tag to cache variables and the add, slugify, and slice filters to generate a new list with only one member.
The following example creates a new list whose sole member is the previous member of the forloop:
{% for item in list %}
{% if not forloop.first %}
{% with forloop.counter0|add:"-1" as previous %}
{% with previous|slugify|add:":"|add:previous as subset %}
{% with list|slice:subset as sublist %}
<p>Current item: {{ item }}</p>
<p>Previous item: {{ sublist.0 }}</p>
{% endwith %}
{% endwith %}
{% endwith %}
{% endif %}
{% endfor %}
This isn't an elegant solution, but the django template system has two faults that make this hack unavoidable for those who don't what to write custom tags:
Django template syntax does not allow nested curly parenthesis. Otherwise, we could do this:
{{ list.{{ forloop.counter|add:-1 }} }}
The lookup operator does not accept values stored using with (and perhaps for good reason)
{% with forloop.counter|add:-1 as index %}
{{ list.index }}
{% endwith %}
This code should work just fine as a django template, as long as object has a property or no-argument method called before, and objects is iterable (and '<' is defined).
{% for object in objects %}
{% if object.before < object %}
this is bigger
{% else %}
this is smaller
{% endfor %}

Django templates - Regrouping by a string parameter

I have the following code in one of my Django templates that I want to refactor:
{% ifequal sort_type "set" %}
{% regroup cards by set as grouped %}
{% endifequal %}
{% ifequal sort_type "rarity" %}
{% regroup cards by rarity as grouped %}
{% endifequal %}
It does work, but it's really ugly and I want to make it more like this:
{% regroup cards by sort_type as groupedcards %}
But this doesn't work (it just puts them all in a single group called None.) From the documentation, I think it might be trying a dictionary lookup (i.e., calling card["set"] instead of card.set).
Is there a good way to do this in the template, or should I move the regrouping out into the Python code using itertools?
Ticked in Django bugtracker related to this problem.