Django cycling templates - django

I need to output objects in Django template, so that each object has its own template. Templates are keeped in the variable "templates" like that - ['path/to/template1','path/to/template2', ...]
Is there a way to "cycle" these templates in object loop, somehow like that:
{% for object in objects %}
{% cycle templates as template %}
{% include template %} // this code is just for example
{% endfor %}
I cannot include these templates directly into objects list, because it is generated by paginator's template tag.
Any ideas? Thanks.

I've written the template tag that receives a list variable and cycles it through the loop.
from django import template
from django.template.base import TemplateSyntaxError, Node
from itertools import cycle as itertools_cycle
register = template.Library()
class CycleListNode(Node):
def __init__(self, list_variable, template_variable):
self.list_variable = list_variable
self.template_variable = template_variable
def render(self, context):
if self not in context.render_context:
# First time the node is rendered in template
context.render_context[self] = itertools_cycle(context[self.list_variable])
cycle_iter = context.render_context[self]
value = cycle_iter.next()
if self.template_variable:
context[self.template_variable] = value
return ''
#register.tag
def cycle_list(parser, token):
args = token.split_contents()
if len(args) != 4 or args[-2] != 'as':
raise TemplateSyntaxError(u"Cycle_list tag should be in the format {% cycle_list list as variable %}")
return CycleListNode(args[1], args[3])
It's pretty simple but solves the issue.

you can create a template tag that does what you want with the templates, and it should take a path ( template's path ) as an argument, then all you would need to do is pass the path template path variable which should be in the context if you are using request context to your custom template tag, like so
{% custom_tag path_to_template_dir %}

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))

How do I set a template argument value to the value of another tag?

I have a template tag to generate the url as follows;
<li>Archive</li>
I want the '2013'(year) to be generated automatically based off the current year. There is a tag that can do this {% now 'Y' %} however i cannot use it inside the existing template tag as it just produces errors.
Do i need to create a custom tag to do this?
May be better decision is would be set default parameter at the urls.py ?
Example urls.py:
from django.utils.timezone import now
...
urlpatterns = patterns('app.views',
url(r'^.../$', 'blog_archive', {'year': now().strftime('%Y')}),
)
It is not possible to include one tag inside another like this.
You can create an assignment tag, which stores the tag's result in a context variable instead of outputting it.
The example assignment tag in the docs is for a template tag get_current_time, which you could use instead of the {% now %} tag.
In your mytags.py template tag module:
from django import template
register = template.Library()
#register.assignment_tag
def get_current_time(format_string):
return datetime.datetime.now().strftime(format_string)
In your template:
{% load mytags %}
{% get_current_time "%Y" as year %}
<li>Archive</li>
That is what you want.
Maybe this:
<li>Archive</li>
# just pass the datetime object as 'value'

Test the return value of a template tag

There's a boolean variable defined in settings.py that I'd like to make visible in my templates, so that I can control whether a part of the template is shown.
I've thought of creating a template tag to expose the value to the template:
#register.simple_tag
def show_something():
return settings.SHOW_SOMETHING
... which I would use in the template like this:
{% if show_something %}
Show it
{% endif %}
... but unfortunately it doesn't seem to work.
Also tried outputting the value, and this displays it as I expected:
{% show_something %}
Any idea if using a template tag is possible for what I need, or if there's a better way?
I think a template context processor might be better suited for this. Put a context_processors.py file in your project
context_processors.py
from django.conf import settings
def some_setting(request):
# Or some other logic here instead of always returning it
return {
'some_setting': settings.SOME_SETTING
}
settings.py
SOME_SETTING = False
TEMPLATE_CONTEXT_PROCESSORS = (
...,
'path.to.context_processors.some_setting'
)
and in your templates you can now access the variable with {{ some_setting }} or use it in an if statement like {% if some_setting %}Show this{% endif %}

if..else custom template tag

I'm implementing a custom permissions application in my Django project, and I'm lost as to how to implement a custom template tag that checks a logged in user's permissions for a specific object instance and shows a piece of HTML based on the outcome of the check.
What I have now is (pseudocode):
{% check_permission request.user "can_edit" on article %}
<form>...</form>
{% endcheck %}
('check_permission' is my custom template tag).
The templatetag takes in the user, the permission and the object instance and returns the enclosed HTML (the form). This currently works fine.
What I would like to do however, is something like:
{% if check_permission request.user "can_edit" on article %}
<form>...</form>
{% else %}
{{ article }}
{% endif %}
I've read about the assignment tag, but my fear is that I would pollute the context variable space with this (meaning I might overwrite previous permission context variables). In other words, as the context variables are being defined on different levels (the view, middleware in my case, and now this assignment template tag), I'm worried about maintainability.
You can use template filters inside if statements. So you could rewrite your tag as a filter:
{% if request.user|check_can_edit:article %}
Note that it's tricky to pass multiple arguments of different types to a filter, so you'll probably want to use one filter per permission, above I've used check_can_edit.
You can definitely do that if you're willing to write some more lines of python code to improve your template readability! :)
You need to parse the tag content yourself, even the parameters it takes and then resolve them, if you want to use variables on them.
The tag implemented below can be used like this:
{% load mytag %}
{% mytag True %}Hi{% else %}Hey{% endmytag %} Bro
Or with a variable:
{% mytag myobject.myflag %}Hi{% else %}Hey{% endmytag %} Bro
So, here's the way I did it:
from django.template import Library, Node, TemplateSyntaxError
register = Library()
#register.tag
def mytag(parser, token):
# Separating the tag name from the "test" parameter.
try:
tag, test = token.contents.split()
except (ValueError, TypeError):
raise TemplateSyntaxError(
"'%s' tag takes two parameters" % tag)
default_states = ['mytag', 'else']
end_tag = 'endmytag'
# Place to store the states and their values
states = {}
# Let's iterate over our context and find our tokens
while token.contents != end_tag:
current = token.contents
states[current.split()[0]] = parser.parse(default_states + [end_tag])
token = parser.next_token()
test_var = parser.compile_filter(test)
return MyNode(states, test_var)
class MyNode(Node):
def __init__(self, states, test_var):
self.states = states
self.test_var = test_var
def render(self, context):
# Resolving variables passed by the user
test_var = self.test_name.resolve(context, True)
# Rendering the right state. You can add a function call, use a
# library or whatever here to decide if the value is true or false.
is_true = bool(test_var)
return self.states[is_true and 'myvar' or 'else'].render(context)
And that's it. HTH.
In Django 2 the assignment tag was replaced by simple_tag() but you could store the custom tag result as a template variable:
# I'm assuming that check_permission receives user and article,
# checks if the user can edit the article and return True or False
{% check_permission user article as permission_cleared %}
{% if permission_cleared %}
<form>...</form>
{% else %}
{{ article }}
{% endif %}
Check the current doc about custom template tags: https://docs.djangoproject.com/en/2.1/howto/custom-template-tags/#simple-tags
inside my_tags.py
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def make_my_variable_true(context):
context['my_variable'] = True
return '' # without this you'll get a "None" in your html
inside my_template.html
{% load my_tags %}
{% make_my_variable_true %}
{% if my_variable %}foo{% endif %}
In this case best solution is to use custom filter. If you don't want write long code for custom tag. Also if you don't want to copy/paste others code.
Here is an example
Inside templatetag
register = template.Library()
def exam_available(user, skill):
skill = get_object_or_404(Skill, id=skill)
return skill.exam_available(user)
register.filter('exam_available', exam_available)
Inside template
{{ request.user|exam:skill.id }}
or
{% if request.user|exam:skill.id %}
Since one of the main common of it is to use request.user or any specific object(id) inside model's custom method, so filtering that individual object or user is the easiest way to make it done. :)

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.