Template rendering (Django): [duplicate] - django

mydict = {"key1":"value1", "key2":"value2"}
The regular way to lookup a dictionary value in a Django template is {{ mydict.key1 }}, {{ mydict.key2 }}. What if the key is a loop variable? ie:
{% for item in list %} # where item has an attribute NAME
{{ mydict.item.NAME }} # I want to look up mydict[item.NAME]
{% endfor %}
mydict.item.NAME fails. How to fix this?

Write a custom template filter:
from django.template.defaulttags import register
...
#register.filter
def get_item(dictionary, key):
return dictionary.get(key)
(I use .get so that if the key is absent, it returns none. If you do dictionary[key] it will raise a KeyError then.)
usage:
{{ mydict|get_item:item.NAME }}

Fetch both the key and the value from the dictionary in the loop:
{% for key, value in mydict.items %}
{{ value }}
{% endfor %}
I find this easier to read and it avoids the need for special coding. I usually need the key and the value inside the loop anyway.

You can't by default. The dot is the separator / trigger for attribute lookup / key lookup / slice.
Dots have a special meaning in template rendering. A dot in a variable
name signifies a lookup. Specifically, when the template system
encounters a dot in a variable name, it tries the following lookups,
in this order:
Dictionary lookup. Example: foo["bar"]
Attribute lookup. Example: foo.bar
List-index lookup. Example: foo[bar]
But you can make a filter which lets you pass in an argument:
https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-filters
#register.filter(name='lookup')
def lookup(value, arg):
return value[arg]
{{ mydict|lookup:item.name }}

For me creating a python file named template_filters.py in my App with below content did the job
# coding=utf-8
from django.template.base import Library
register = Library()
#register.filter
def get_item(dictionary, key):
return dictionary.get(key)
usage is like what culebrón said :
{{ mydict|get_item:item.NAME }}

I had a similar situation. However I used a different solution.
In my model I create a property that does the dictionary lookup. In the template I then use the property.
In my model: -
#property
def state_(self):
""" Return the text of the state rather than an integer """
return self.STATE[self.state]
In my template: -
The state is: {{ item.state_ }}

Environment: Django 2.2
Example code:
from django.template.defaulttags import register
#register.filter(name='lookup')
def lookup(value, arg):
return value.get(arg)
I put this code in a file named template_filters.py in my project folder named portfoliomgr
No matter where you put your filter code, make sure you have __init__.py in that folder
Add that file to libraries section in templates section in your projectfolder/settings.py file. For me, it is portfoliomgr/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'libraries':{
'template_filters': 'portfoliomgr.template_filters',
}
},
},
]
In your html code load the library
{% load template_filters %}

Since I can't comment, let me do this in the form of an answer:
to build on culebrón's answer or Yuji 'Tomita' Tomita's answer, the dictionary passed into the function is in the form of a string, so perhaps use ast.literal_eval to convert the string to a dictionary first, like in this example.
With this edit, the code should look like this:
# code for custom template tag
#register.filter(name='lookup')
def lookup(value, arg):
value_dict = ast.literal_eval(value)
return value_dict.get(arg)
<!--template tag (in the template)-->
{{ mydict|lookup:item.name }}

env: django 2.1.7
view:
dict_objs[query_obj.id] = {'obj': query_obj, 'tag': str_tag}
return render(request, 'obj.html', {'dict_objs': dict_objs})
template:
{% for obj_id,dict_obj in dict_objs.items %}
<td>{{ dict_obj.obj.obj_name }}</td>
<td style="display:none">{{ obj_id }}</td>
<td>{{ forloop.counter }}</td>
<td>{{ dict_obj.obj.update_timestamp|date:"Y-m-d H:i:s"}}</td>

Related

How to display Model in each HTML template?

But create a request in each functions inside views.py I do not want.
Is it possible to output the model in each template using only one request?
I tried to use templateetags but
that so
#register.simple_tag(takes_context=True)
def header_categories(context):
return Categorie.objects.all().order_by('id')
what is like that
#register.simple_tag(takes_context=True)
def header_categories(context):
categories = Categorie.objects.all().order_by('id')
args = {}
for cat in categories:
args[cat.text] = {
'id':cat.id,
}
if cat.parent:
args[cat.text]['parent_id'] = cat.parent.id
args[cat.text]['parent_text'] = cat.parent.text
return args
Nothing works correctly
{% for cat in header_categories %}
cat.text
{% endfor %}
I tried through js
var arr = {%header_categories%}
but django changes everything
{'dresses': {'id': 19},
Before going deeper into your question, I think you should have
{% for cat in header_categories %}
{{ cat.text }}
{% endfor %}
You need to make a custom context processor (See Using RequestContext [Django docs]). What this would do is add a variable to the context for each template. Quoting the documentation:
The context_processors option is a list of callables – called
context processors – that take a request object as their argument and return a dictionary of items to be merged into the context.
In some suitable app of yours make a file named context_processors.py and add this code in it:
def header_categories(request):
return {'header_categories': Categorie.objects.all().order_by('id')}
Now in your settings in the TEMPLATES settings add this context processor to the list context_processors, so it should look like:
[
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'your_app.context_processors.header_categories',
]
Now in your templates you can simply write:
{% for cat in header_categories %}
{{ cat.text }}
{% endfor %}

Django: check for exact url in request using regex

I need to check if the url is exactly:
http://127.0.0.1:8000/shop/
and based on this render a header.
If it is, for example:
http://127.0.0.1:8000/shop/stickers/stickers-por-hoja/medida-y-cantidad
Header shouldn't render.
I've read you can do something like this:
{% if "/shop/$" in request.path %}
{% include 'header.html' %}
{% endif %}
But this doesn't work. On the other hand this works, but is not what I need:
{% if "/shop/" in request.path %}
{% include 'header.html' %}
{% endif %}
So, Can I use regex in the if condition?
Create a context processor has_shop_in_url.py having this code below.
import re
def has_shop(request):
shop_in_request = your_regex_validation
return {
'has_shop_in_url': any([shop_in_request]) }
Add that context processor
TEMPLATES = [
{
.....
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'your_module.has_shop_in_url.has_shop' # YOUR PROCESSOR
],
},
},
]
Now it is available for your whole web application. In template just write {{has_shop_in_url}}. Your can see True/False.
You could move the regex check into the view and add a field to the context for the template like so:
class MyView(View):
def action(self, request, *args, **kwargs):
shop_in_request = re.findall(r"/shop/$", request.path)
context = {"include_header": any(shop_in_request)}
return render(template, context)
Then in your view you could use:
{% if include_header %}
{% include 'header.html' %}
{% endif %}
Create a template tag to do that.
The url whitelisted must be somewhere in the settings. For example you can add something like this
HEADER_WHITELIST_URLS = (
'regex1',
'regex2',
)
2) Your template tags will check the current URL and if one of the regex matches, you can render your header. In a hypothetical version2, you can also add the header to show for a specific regex.
Use this snippet as starting point for your template tag
import datetime
from django import template
from django.conf import settings
register = template.Library()
#register.simple_tag(takes_context=True)
def render_header(context, format_string):
regexs = settings.HEADER_WHITELIST_URLS
# maybe you can add some try/exception if the settings is not available.
for regex in regexs:
# test the regex with the url in the context.
# if matches return the code to render the header and break
return None
Check here for more details about the template tags: https://docs.djangoproject.com/en/2.1/howto/custom-template-tags/#writing-custom-template-tags

Want to resolve a dictionary whose key is a field in a model [duplicate]

mydict = {"key1":"value1", "key2":"value2"}
The regular way to lookup a dictionary value in a Django template is {{ mydict.key1 }}, {{ mydict.key2 }}. What if the key is a loop variable? ie:
{% for item in list %} # where item has an attribute NAME
{{ mydict.item.NAME }} # I want to look up mydict[item.NAME]
{% endfor %}
mydict.item.NAME fails. How to fix this?
Write a custom template filter:
from django.template.defaulttags import register
...
#register.filter
def get_item(dictionary, key):
return dictionary.get(key)
(I use .get so that if the key is absent, it returns none. If you do dictionary[key] it will raise a KeyError then.)
usage:
{{ mydict|get_item:item.NAME }}
Fetch both the key and the value from the dictionary in the loop:
{% for key, value in mydict.items %}
{{ value }}
{% endfor %}
I find this easier to read and it avoids the need for special coding. I usually need the key and the value inside the loop anyway.
You can't by default. The dot is the separator / trigger for attribute lookup / key lookup / slice.
Dots have a special meaning in template rendering. A dot in a variable
name signifies a lookup. Specifically, when the template system
encounters a dot in a variable name, it tries the following lookups,
in this order:
Dictionary lookup. Example: foo["bar"]
Attribute lookup. Example: foo.bar
List-index lookup. Example: foo[bar]
But you can make a filter which lets you pass in an argument:
https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-filters
#register.filter(name='lookup')
def lookup(value, arg):
return value[arg]
{{ mydict|lookup:item.name }}
For me creating a python file named template_filters.py in my App with below content did the job
# coding=utf-8
from django.template.base import Library
register = Library()
#register.filter
def get_item(dictionary, key):
return dictionary.get(key)
usage is like what culebrón said :
{{ mydict|get_item:item.NAME }}
I had a similar situation. However I used a different solution.
In my model I create a property that does the dictionary lookup. In the template I then use the property.
In my model: -
#property
def state_(self):
""" Return the text of the state rather than an integer """
return self.STATE[self.state]
In my template: -
The state is: {{ item.state_ }}
Environment: Django 2.2
Example code:
from django.template.defaulttags import register
#register.filter(name='lookup')
def lookup(value, arg):
return value.get(arg)
I put this code in a file named template_filters.py in my project folder named portfoliomgr
No matter where you put your filter code, make sure you have __init__.py in that folder
Add that file to libraries section in templates section in your projectfolder/settings.py file. For me, it is portfoliomgr/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'libraries':{
'template_filters': 'portfoliomgr.template_filters',
}
},
},
]
In your html code load the library
{% load template_filters %}
Since I can't comment, let me do this in the form of an answer:
to build on culebrón's answer or Yuji 'Tomita' Tomita's answer, the dictionary passed into the function is in the form of a string, so perhaps use ast.literal_eval to convert the string to a dictionary first, like in this example.
With this edit, the code should look like this:
# code for custom template tag
#register.filter(name='lookup')
def lookup(value, arg):
value_dict = ast.literal_eval(value)
return value_dict.get(arg)
<!--template tag (in the template)-->
{{ mydict|lookup:item.name }}
env: django 2.1.7
view:
dict_objs[query_obj.id] = {'obj': query_obj, 'tag': str_tag}
return render(request, 'obj.html', {'dict_objs': dict_objs})
template:
{% for obj_id,dict_obj in dict_objs.items %}
<td>{{ dict_obj.obj.obj_name }}</td>
<td style="display:none">{{ obj_id }}</td>
<td>{{ forloop.counter }}</td>
<td>{{ dict_obj.obj.update_timestamp|date:"Y-m-d H:i:s"}}</td>

Determine variable type within django template

I have a variable that I'm pulling into a table that sometimes is a date and sometimes is a string. If the variable is a date, I want to change the formatting:
<td>{{ action.extra_column|date:"M d" }}</td>
But if it is a string, I just want to display it as is:
<td>{{ action.extra_column }}</td>
If I try to format it and it is a string, I get no output for the variable.
How can I determine the type so that I can adjust my rendering based on type.
You could also implement a general template filter as an equivalent to the type() builtin:
# app/templatetags/util.py
from django import template
register = template.Library()
#register.filter
def get_type(value):
return type(value)
# template.html
{% load util %}
{% if extra_column|get_type == 'str' %}
String
{% elif extra_column|get_type == 'datetime.date' %}
Date
{% else %}
Oh no!
{% endif %}
I think Ignacio and Dirk are right, however. Can't you just have two keys (you say "array", but I assume you mean "dictionary" from the fact that the items have names) called date and detail?
# views.py
...
actions = [{
'some_property': 'some_value'
'date': None,
'detail': 'details'
},
{
'some_property': 'some_value'
'date': datetime.date.today(),
'detail': None
}]
...
# template.html
{% for action in actions %}
<td>{% if action.date %}{{ action.date|date:"M d" }}{% endif %}{{ action.detail }}</td>
{% endfor %}
# output
<td>details</td>
<td>Aug 19</td>
Like Ignacio Vazquez-Abrams pointed out in the first comment, that's not really a great way to code your logic. I would ensure that your variable has a certain type. That could be solved through an additional variable you add to the context or an object that holds the data and something that describes the type of data.
If you want to stick to your logic, a possible approach would be to write your own template filter (let's call it date_or_string). The filter could subclass the builtin date filter with the format parameter being optional. In case the parameter is passed it works like the normal date filter, without the parameter it simply returns the string. In a more complex scenario the filter could also do some type checking. Just an idea, i wouldn't actually put that kind of logic into the template.
I know I'm way behind on this (by three years) but I just got here looking to do something similar and came up with what I think is a decent solution.
Just add a function to your models like get_model_type and have it return something you'd expect from each model like so:
class MyModelOne(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=255)
def get_model_type(self):
return "my_model_one"
class MyModelTwo(models.Model):
date_created = models.DateTimeField(auto_now_add=True)
other_field = models.CharField(max_length=255)
def get_model_type(self):
return "my_model_two"
Then in your template you can easily just call that function:
{% if model.get_model_type == 'my_model_one' %}
<p>Model One</p>
{% elif model.get_model_type == 'my_model_two' %}
<p>Model Two</p>
{% endif %}
Late to the party, but I just had this problem. The solution I went for is duck-typing, so:
{% if action.extra_column.year %}
{{ action.extra_column|date:"M y" }}
{% else %}
{{ action.extra_column }}
{% endif %}
Could you argue that this is definitely not the right way to do it? Probably. Will it get the job done without writing your own template filter and having even more code to maintain? Absolutely.
That's my approach:
#register.filter
def get_type(value):
""" It returns variable type as a pure string name """
return type(value).__name__
You can try this to recognize String vs List type:
{%if v_1.0.1|length == 0%}
<!--STR-->
{{v_1}}
{%else%}
<!--List-->
{{v_1.0}}
{%endif%}

django template inheritance and context

I am reading the definitive guide to django and am in Chapter 4 on template inheritance. It seems that I am not doing something as elegant as should be possible as I am having to duplicate some code for the context to appear when calling the child view. Here is the code in views.py:
def homepage(request):
current_date = datetime.datetime.now()
current_section = 'Temporary Home Page'
return render_to_response("base.html", locals())
def contact(request):
current_date = datetime.datetime.now()
current_section = 'Contact page'
return render_to_response("contact.html", locals())
It seems redundant to have to include the current_date line in each function.
Here is the base html file that homepage calls:
<html lang= "en">
<head>
<title>{% block title %}Home Page{% endblock %}</title>
</head>
<body>
<h1>The Site</h1>
{% block content %}
<p> The Current section is {{ current_section }}.</p>
{% endblock %}
{% block footer %}
<p>The current time is {{ current_date }}</p>
{% endblock %}
</body>
</html>
and a child template file:
{% extends "base.html" %}
{% block title %}Contact{% endblock %}
{% block content %}
<p>Contact information goes here...</p>
<p>You are in the section {{ current_section }}</p>
{% endblock %}
If I don't include the current_date line when calling the child file, where that variable should appear is blank.
You can pass a variable to every template by using a Context Processor:
1. Adding the context processor to your settings file
First, you will need to add your custom Context Processor to your settings.py:
# settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
'myapp.context_processors.default', # add this line
'django.core.context_processors.auth',
)
From that you can derive that you will need to create a module called context_processors.py and place it inside your app's folder. You can further see that it will need to declare a function called default (as that's what we included in settings.py), but this is arbitrary. You can choose whichever function name you prefer.
2. Creating the Context Processor
# context_processors.py
from datetime import datetime
from django.conf import settings # this is a good example of extra
# context you might need across templates
def default(request):
# you can declare any variable that you would like and pass
# them as a dictionary to be added to each template's context:
return dict(
example = "This is an example string.",
current_date = datetime.now(),
MEDIA_URL = settings.MEDIA_URL, # just for the sake of example
)
3. Adding the extra context to your views
The final step is to process the additional context using RequestContext() and pass it to the template as a variable. Below is a very simplistic example of the kind of modification to the views.py file that would be required:
# old views.py
def homepage(request):
current_date = datetime.datetime.now()
current_section = 'Temporary Home Page'
return render_to_response("base.html", locals())
def contact(request):
current_date = datetime.datetime.now()
current_section = 'Contact page'
return render_to_response("contact.html", locals())
# new views.py
from django.template import RequestContext
def homepage(request):
current_section = 'Temporary Home Page'
return render_to_response("base.html", locals(),
context_instance=RequestContext(request))
def contact(request):
current_section = 'Contact page'
return render_to_response("contact.html", locals(),
context_instance=RequestContext(request))
So, you can use django.views,generic.simple.direct_to_template instead of render_to_response. It uses RequestContext internaly.
from django.views,generic.simple import direct_to_template
def homepage(request):
return direct_to_template(request,"base.html",{
'current_section':'Temporary Home Page'
})
def contact(request):
return direct_to_template(request,"contact.html",{
'current_section':'Contact Page'
})
Or you can even specify it directly at urls.py such as
urlpatterns = patterns('django.views.generic.simple',
(r'^/home/$','direct_to_template',{
'template':'base.html'
'extra_context':{'current_section':'Temporary Home Page'},
}),
(r'^/contact/$','direct_to_template',{
'template':'contact.html'
'extra_context':{'current_section':'Contact page'},
}),
For django v1.8+ variables returned inside context processor can be accessed.
1. Add the context processor to your TEMPLATES list inside settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'your_app.context_processor_file.func_name', # add this line
],
},
},
]
2. Create new file for context processor and define method for context
context_processor_file.py
def func_name(request):
test_var = "hi, this is a variable from context processor"
return {
"var_for_template" : test_var,
}
3. Now you can get the var_for_template in any templates
for example, add this line inside: base.html
<h1>{{ var_for_template }}</h1>
this will render:
<h1>hi, this is a variable from context processor</h1>
for updating templates to django 1.8+ follow this django doc