Access template passed variables in Django - django

I pass to the template:
testruns, which is "get_list_or_404(TestRun)"
and
dict, which is smth like this:
for testrun in testruns:
dict[testrun.id] = {
'passed' : bla bla,
'failed' : bla bla 2
}
Practically a map between testrun.id and some other info from a set from TestRun Model
In the template I want to do this:
{% for testrun in testruns %}
console.log("{{ dict.testrun.id }}");
{% endfor %}
But doesn't output anything
console.log("{{ testrun.id }}"); will output a specific id ("37" for example)
console.log("{{ dict.37 }}"); will output the corresponding value from the dict
So, why doesn't this output anything?
console.log("{{ dict.testrun.id }}");
How should I get the data from 'passed' and 'failed' from the dict:
Also, this:
console.log("{{ dict[testrun.id] }}");
Will output this error:
TemplateSyntaxError at /path/dashboard
Could not parse the remainder: '[testrun.id]' from 'dict[testrun.id]'

The dot will be considered as a trigger for attribute lookup by template engine, so dict.testrun.id will be parsed as "try to find id attribute from testrun attribute from dict". Instead, if you want to show the whole dict content, you might just iterate through dictionary:
{% for key, value in dict.items %}
Testcase: {{ key }}
Passed: {{ value.passed }}
Failed: {{ value.failed }}
{% endfor %}
Or, if you are looking for dict lookup by variable's value, you will have to make custom template tag, like it was described here - Django template how to look up a dictionary value with a variable

Related

Was wondering why iterating through a dictionary using .keys in django would not work?

I know that .items would be useful to grab the value, but wanted to see why this would not work?
Data:
...
city_data = {
'city': json_data['name'],
'country': json_data['sys']['country'],
'temp': json_data['main']['temp'],
'feels_like': json_data['main']['feels_like'],
'temp_max': json_data['main']['temp_max'],
'temp_min': json_data['main']['temp_min']
}
return render(request, ..., context={'city_data':city_data})
template:
...
{% for key in city_data.keys %}
<li>{{city_data.key}}</li>
{% endfor %}
...
I think that the reason that it doesn't work that way is because django will look at test.key and try to look up a string "key" as an actual key to the dictionary. There are a couple ways that you could do this. One way is you could define a custom template filter that would allow you to do it. I don't know much about custom filters so I can't say how specifically to do it. Another way though is to use city_data.items in your template instead like this:
{% for key,value in city_data.items %}
<li>{{ value }}</li>
{% endfor %}

What's the most DRY way to disable a link to the current page?

In my Django web-app, I want to let users sort model objects by different parameters, which I achieve with URL parameters which tell the view which items should be loaded. Here's the Jinja/HTML snippet from the template:
<p><b><span class="text-info">sort by:</span></b>
latest_release |
alphabetically |
soonest release</p>
If the user is already sorting by latest_release (the first link), I want the link from it removed. However, I can't seem to find a way to do this in a DRY way.
You can define dict in your view with argument name - display name mapping:
mapping = {'': 'latest_release', 'name': 'alphabetically', 'next_release': 'soonest release'}
and pass it to context:
context['mapping'] = mapping
Now in template iterate over each pair from dict and show link only if sorted_by value not equal with key:
{% for k, v in mapping.items %}
{% if request.GET.sorted_by|default:"" != k %} {{ v }} |{% endif %}
{% endfor %}
To remove | delimiter after last link you can validate forloop.last status.

Loop over a sorted dictionary a specific number of times in a django template

I have a sorted dictionary that contains sort options:
sort_options = SortedDict([
("importance" , ("Importance" , "warning-sign")),
("effort" , ("Effort" , "wrench" , "effort")),
("time_estimate" , ("Time Estimate" , "time")),
("date_last_completed" , ("Date Last Completed" , "calendar")),
])
I'm displaying these options in my template:
{% for key, icon in sort_options.items %}<!-- Sort Options -->
<a class="btn btn-info" href={{ request.path }}?key={{ key }}&orientation=desc><i class="icon-{{ icon.1 }} icon-large"></i></a>
{% endfor %}
I need to define the 4 sort options, but I only want to display the first 3 (the remaining options are used elsewhere). I also anticipate adding other sort options that I won't need to be displayed. I could write an if statement with a forloop counter to prevent the last option from displaying, but this seems wasteful.
I found this filter but I'm not sure how to combine it with the forloop that needs both the key and the icon data.
How can I write a django template for loop that runs on a dictionary and only loops X number of times?
Similar to Joe's answer, but there's actually a built-in filter slice that'll do this for you:
{% for key, icon in sort_options.items|slice:":3" %}
I think you could do this with a template filter. For example, in:
./mymodules/templatetags/mytags.py
#register.filter
def get_recent(object, token):
"""
Must pass a Option Dictionary
"""
return object.items()[:token]
And then in your template:
{% load mytags %}
{% for option in sort_options|get_recent:3 %}
key: {{ option.0 }}
value: {{ option.1 }}
{% endfor %}
I haven't had a chance to test the above code, but think the logic is sound. Let me know what you think.

Need to convert a string to int in a django template

I am trying to pass in url parameters to a django template like this...
response = render_to_string('persistConTemplate.html', request.GET)
This the calling line from my views.py file. persistConTemplate.html is the name of my template and request.GET is the dictionary that contains the url parameters.
In the template I try to use one of the parameters like this...
{% for item in (numItems) %}
item {{item}}
{% endfor %}
numItems is one of the url parameters that I am sending in my request like this...
http:/someDomain/persistentConTest.html/?numItems=12
When I try the for loop above, I get an output like this....
image 1 image 2
I am expecting and would like to see the word image printed 12 times...
image 1 image 2 image 3 image 4 image 5 image 6 image 7 image 8 image 9 image 10 image 11 image 12
Can anyone please tell me what I am going wrong?
you can coerce a str to an int using the add filter
{% for item in numItems|add:"0" %}
https://docs.djangoproject.com/en/dev/ref/templates/builtins/#add
to coerce int to str just use slugify
{{ some_int|slugify }}
EDIT: that said, I agree with the others that normally you should do this in the view - use these tricks only when the alternatives are much worse.
I like making a custom filter:
# templatetags/tag_library.py
from django import template
register = template.Library()
#register.filter()
def to_int(value):
return int(value)
Usage:
{% load tag_library %}
{{ value|to_int }}
It is for cases where this cannot be easily done in view.
Yes, the place for this is in the view.
I feel like the above example won't work -- you can't iterate over an integer.
numItems = request.GET.get('numItems')
if numItems:
numItems = range(1, int(numItems)+1)
return direct_to_template(request, "mytemplate.html", {'numItems': numItems})
{% for item in numItems %}
{{ item }}
{% endfor %}
The easiest way to do this is using inbuilt floatformat filter.
For Integer
{{ value|floatformat:"0" }}
For float value with 2 precision
{{ value|floatformat:"2" }}
It will also round to nearest value. for more details, you can check https://docs.djangoproject.com/en/1.10/ref/templates/builtins/#floatformat.
You should add some code to your view to unpack the GET params and convert them to the values you want. Even if numItems were an integer, the syntax you're showing wouldn't give you the output you want.
Try this:
ctx = dict(request.GET)
ctx['numItems'] = int(ctx['numItems'])
response = render_to_string('persistConTemplate.html', ctx)
In my case one of the items was a string and you can not compare a string to an integer so I had to coerce the string into an integer see below
{% if questions.correct_answer|add:"0" == answers.id %}
<span>Correct</span>
{% endif %}
You can do like that: if "select" tag used.
{% if i.0|stringformat:'s' == request.GET.status %} selected {% endif %}
My solution is kind of a hack and very specific..
In the template I want to compare a percentage with 0.9, and it never reaches 1, but all the values are considered string in the template, and no way to convert string to float.
So I did this:
{% if "0.9" in value %}
...
{% else %}
...
{% endif %}
If I want to detect some value is beyond 0.8, I must do:
{% if ("0.9" in value) or ("0.8" in value) %}
...
{% else %}
...
{% endif %}
This is a hack, but suffice in my case. I hope it could help others.

Django - use template tag and 'with'?

I have a custom template tag:
def uploads_for_user(user):
uploads = Uploads.objects.filter(uploaded_by=user, problem_upload=False)
num_uploads = uploads.count()
return num_uploads
and I'd like to do something like this, so I can pluralize properly:
{% with uploads_for_user leader as upload_count %}
{{ upload_count }} upload{{ upload_count|pluralize }}
{% endwith %}
However, uploads_for_user leader doesn't work in this context, because the 'with' tag expects a single value - Django returns:
TemplateSyntaxError at /upload/
u'with' expected format is 'value as name'
Any idea how I can get round this?
You could turn it into a filter:
{% with user|uploads_for as upload_count %}
While a filter would still work, the current answer to this question would be to use assignment tags, introduced in Django 1.4.
So the solution would be very similar to your original attempt:
{% uploads_for_user leader as upload_count %}
{{ upload_count }} upload{{ upload_count|pluralize }}
Update: As per the docs assignment tags are deprecated since Django 1.9 (simple_tag can now store results in a template variable and should be used instead)
In Django 1.9 django.template.Library.assignment_tag() is depricated:
simple_tag can now store results in a template variable and should be used instead.
So, now simple tag we can use like a:
It’s possible to store the tag results in a template variable rather
than directly outputting it. This is done by using the as argument
followed by the variable name. Doing so enables you to output the
content yourself where you see fit:
{% get_current_time "%Y-%m-%d %I:%M %p" as the_time %}
<p>The time is {{ the_time }}.</p>