Force Django template to render curly brace values? - django

I have this data that comes from a field in the database:
item_list = Links.objects.filter(visible=True)
In an iteration of item_list there is item.name and item.link. In item.link there could potentially be a string value of
'/app/user/{{user.id}}/'.
When rendering this particular item.link in a Django template, it comes out literally in html output as:
/app/user/{{user.id}}/
when literally I am hoping for it to render as:
/app/user/1/
Is there any way to force the template to recognize this as a compiled value for output?

You have to create a custom template tag:
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def render(context, tpl_string):
t = template.Template(tpl_string)
return t.render(context)
And the in your template:
{% load my_tags %}
{% render item.link %}

Related

Using template tag in ModelAdmin.readonly_fields method

I use this pattern:
class PersonAdmin(admin.ModelAdmin):
readonly_fields = ('address_report',)
def address_report(self, instance):
return format_html(...)
Source: docs about readonly_fields
Now I would like to use a custom templatetag in the Python method address_report().
What is the best way to call it?
I tried to called my templatetag directly, but this just returns a dictionary, not html.
I think you'll have to create a template from string. That way you can use any template tags or variables you want.
Example:
from django.template import Template, Context
def address_report(...):
# create template
t = Template("""
{% load custom_tag_module %}
<p>
Hello, {{ name }}! <br>
{% custom_tag %}
</p>
""")
# create context
c = Context({'name': 'World'})
# render template, mark safe and return
return mark_safe(t.render(c))

get_FOO_display equivalent for ChoiceBlock

I have a block as follows:
class SomeBlock(blocks.StructBlock):
choice = blocks.ChoiceBlock(choices=(('Y', 'Yellow'), ...))
# more fields
class Meta:
template = 'myapp/blocks/some_block.html'
In my template I have:
{% load wagtailcore_tags %}
<div>{{ value.choice }}</div>
This would display 'Y' as expected but how do I get it to display as 'Yellow'?
These variations do not work (get no output):
{{ value.get_choice_display }}
{{ value.bound_blocks.get_choice_display }}
Unfortunately there isn't a direct equivalent - Wagtail's blocks mechanism treats the display names as a detail that's specific to the edit form, rather than part of the data, so they're not easily accessible at the point where you're rendering the template. I'd suggest arranging things as follows:
Define the choice list in its own module where it can be imported from multiple places - e.g. myapp/colors.py:
COLORS = (('Y', 'Yellow'), ...)
COLORS_LOOKUP = dict(COLORS) # gives you a dict of the form {'Y': 'Yellow', ...}
Update the ChoiceBlock definition to refer to the list defined there:
from myapp.colors import COLORS
class SomeBlock(blocks.StructBlock):
choice = blocks.ChoiceBlock(choices=COLORS)
Create a custom template tag to do the lookup from value to display name - e.g. myapp/templatetags/color_tags.py:
from django import template
from myapp.colors import COLORS_LOOKUP
register = template.Library()
#register.simple_tag
def get_color_display_name(name):
return COLORS_LOOKUP.get(name)
Use this tag in your template:
{% load color_tags %}
...
{% get_color_display_name value.choice %} {# to output it immediately #}
{% get_color_display_name value.choice as label %} {# to assign it to the variable 'label' #}

How to pass context variable in a ModelField?

I'm using Django 1.8.
I want to use a CharField with an internal context variable.
For example,
in models.py
...
content = models.CharField(max_length=200)
...
in template:
...
{{ model_instance.content }}
...
The output in html:
...
The content of a context variable is {{ request.variable }}
...
But I want to get:
...
The content of a context variable is test-test-test
...
How can I accomlpish it? I used {{ model_instance.content|safe }}, but it had no effect.
A simple template tag will do it:
yourapp/templatetags/yourapp_tags.py
from django.template import Template
#register.simple_tag(takes_context=True)
def render(context, content):
return Template(content).render(context)
yourapp/templates/template.html
{% load yourapp_tags %}
{% render model_instance.content %}
Just make sure your content fields are not writable for plain site users, as it imposes a security risk.

Django: problems while loading custom filters in the base template file while using template inheritance

When doing the {% load custom_filters %} in the template, after {% extends "base.html" %} everything works fine, but when I move the load to the base.html template the filter gets a weird behaviour. This is my custom_filters.py:
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
# To cut off strings at a specified character, at first occurance. Example:
# time = 19:30:12.123456
# {{ time|cut:'.' }}
# returns: 19:30:12
#register.filter
#stringfilter
def cut(string, cutoff_point):
return string.split(cutoff_point, 1)[0]
When I load it in the 'end-template' the behaviour is as expected. If time = 19:30:12.123456 then {{ time|cut:'.' }} returns 19:30:12. When I load it in base.html the returned value is 19:30:12123456, the same as the input but without the 'cutoff-point'.
Does anyone know why?
You should place {% load ... %} in every template, where you want to use your custom tag or filter.
In your case it's also not a good idea to call a filter cut, because this filter already exists (and it's used to cut a dot from your string).

Check if a template exists within a Django template

Is there an out-of-the-box way of checking if a template exists before including it in a Django template? Alternatives are welcome too but some of them would not work due to the particular circumstances.
For example, here's an answer to a slightly different question. This is not what I'm looking for:
How to check if a template exists in Django?
Assuming include doesn't blow up if you pass it a bad template reference, that's probably the best way to go. Your other alternative would be to create a template tag that essentially does the checks in the link you mentioned.
Very basic implementation:
from django import template
register = template.Library()
#register.simple_tag
def template_exists(template_name):
try:
django.template.loader.get_template(template_name)
return "Template exists"
except template.TemplateDoesNotExist:
return "Template doesn't exist"
In your template:
{% template_exists 'someapp/sometemplate.html' %}
That tag isn't really all that useful, so you'd probably want to create one that actually adds a variable to the context, which you could then check in an if statement or what not.
I encountered this trying to display a template only if it exists, and wound up with the following template tag solution:
Include a template only if it exists
Put the following into yourapp/templatetags/include_maybe.py
from django import template
from django.template.loader_tags import do_include
from django.template.defaulttags import CommentNode
register = template.Library()
#register.tag('include_maybe')
def do_include_maybe(parser, token):
"Source: http://stackoverflow.com/a/18951166/15690"
bits = token.split_contents()
if len(bits) < 2:
raise template.TemplateSyntaxError(
"%r tag takes at least one argument: "
"the name of the template to be included." % bits[0])
try:
silent_node = do_include(parser, token)
except template.TemplateDoesNotExist:
# Django < 1.7
return CommentNode()
_orig_render = silent_node.render
def wrapped_render(*args, **kwargs):
try:
return _orig_render(*args, **kwargs)
except template.TemplateDoesNotExist:
return CommentNode()
silent_node.render = wrapped_render
return silent_node
Access it from your templates by adding {% load include_maybe %} at the top of your template, and using {% include_maybe "my_template_name.html" %} in code.
This approach has the nice side effect of piggy-backing the existing template include tag, so you can pass in context variables in the same way that you can with a plain {% include %}.
Switch based on whether a template exists
However, I wanted some additional formatting on the embedding site if the template existed. Rather than writing an {% if_template_exists %} tag, I wrote a filter that lets you work with the existing {% if %} tag.
To this end, put the following into yourapp/templatetags/include_maybe.py (or something else)
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
#register.filter
#stringfilter
def template_exists(value):
try:
template.loader.get_template(value)
return True
except template.TemplateDoesNotExist:
return False
And then, from your template, you can do something like:
{% load include_maybe %}
{% if "my_template_name"|template_exists %}
<div>
<h1>Notice!</h1>
<div class="included">
{% include_maybe "my_template_name" %}
</div>
</div>
{% endif %}
The advantage of using a custom filter over using a custom tag is that you can do things like:
{% if "my_template_name"|template_exists and user.is_authenticated %}...{% endif %}
instead of using multiple {% if %} tags.
Note that you still have to use include_maybe.
I needed to conditionally include templates if they exist but I wanted to use a variable to store the template name like you can do with the regular {% include %} tag.
Here's my solution which I've used with Django 1.7:
from django import template
from django.template.loader_tags import do_include
register = template.Library()
class TryIncludeNode(template.Node):
"""
A Node that instantiates an IncludeNode but wraps its render() in a
try/except in case the template doesn't exist.
"""
def __init__(self, parser, token):
self.include_node = do_include(parser, token)
def render(self, context):
try:
return self.include_node.render(context)
except template.TemplateDoesNotExist:
return ''
#register.tag('try_include')
def try_include(parser, token):
"""
Include the specified template but only if it exists.
"""
return TryIncludeNode(parser, token)
In a Django template it might be used like this:
{% try_include "template-that-might-not-exist.html" %}
Or, if the template name is in a variable called template_name:
{% try_include template_name %}
include accepts variables:
{% include template_name %}
so you could do the check in your view.