Reputation: 12571
I have a custom Django template tag that acts as a conditional block:
{% if_has_permission request "some_permission" %}
<div>
<input type="text" name="sample_1">
<label><input type="checkbox" name="enable_it"> Enable</label>
</div>
{% endif_has_permission %}
In this example, if the request object doesn't have the appropriate permission (some_permission
in this case), the block doesn't get rendered. However, as soon as I inject a conditional into this block (using the {% if %}
template tag), I get a TemplateSyntaxError:
{% if_has_permission request "some_permission" %}
<div>
<input type="text" name="sample_1">
<label><input type="checkbox" name="enable_it" {% if isChecked %}checked="checked"{% endif %}> Enable</label>
</div>
{% endif_has_permission %}
The error I see is:
Invalid block tag: 'endif', expected 'endblock'
What, if anything, can I do to allow conditional expressions within my custom tag? I'm pretty sure that {% if %}
is the only case I'll ever need to allow, though the occasional {% for %}
might also be useful.
Here's my custom template tag code:
@register.tag
def if_has_permission(parser, token):
try:
args = token.split_contents()
tag_name, request, to_check = args[0], args[1], args[2]
opts = None
if(len(args) > 3):
opts = args[3:]
except IndexError:
raise template.TemplateSyntaxError("Tag %r requires at least two arguments" % tag_name)
if(not (to_check[0] == to_check[-1] and to_check[0] in ('"', "'"))):
raise template.TemplateSyntaxError("The second argument to tag %r must be in quotes" % tag_name)
nodelist_true = parser.parse(('endif_has_permission'),)
parser.delete_first_token()
return CheckPermissionNode(request, to_check[1:-1], opts, nodelist_true)
class CheckPermissionNode(template.Node):
def __init__(self, request, to_check, opts, nodelist_true):
self.request = template.Variable(request)
self.to_check = to_check
self.opts = opts
self.nodelist_true = nodelist_true
def render(self, context):
rq = self.request.resolve(context)
# Admins can always see everything
if(rq.session['is_admin']):
return self.nodelist_true.render(context)
# Check to see if any of the necessary permissions are present
hasPerm = False
checkList = self.to_check.split('|')
for c in checkList:
if(c in rq.session['perms']):
hasPerm = True
break
if(hasPerm):
return self.nodelist_true.render(context)
else:
return ''
Upvotes: 1
Views: 1907
Reputation: 12571
As it turns out, this is indeed possible. There's a typo in the if_has_permission
routine:
nodelist_true = parser.parse(('endif_has_permission'),)
should instead become:
nodelist_true = parser.parse(('endif_has_permission',))
Note that the comma was in the wrong place! The parse
function expects a tuple. Fixing this typo prevents things from going awry.
As an aside, I stumbled upon this question today after running into the exact same problem. Imagine my surprise when I found that I was the original asker, nearly five years ago; ha!
Upvotes: 1
Reputation: 13571
Templatetags are not like blocks - think about them more like as methods. The error you are getting is due to bad syntax then.
To implement something like this just create a filter that will check a condition (exactly like your tag is doing now) and will return True or False then use it like
{% if request|your_filter_name:"condition" %}
<p> do_sth </p>
{% endif %}
by using default django-template if block
Please notice that you cannot use tags in if blocks - that's why you need to change it to being filter (by adding register.filter
instead of register.tag
). Nothing will change but syntax:
request|your_filter:"condition"
instead of
your_tag request "condition"
Upvotes: 1