[Django]-How do I write a Django template tag for access control?

3đź‘Ť

âś…

OK, this is how I did it…

The tag is used like this in the template:

   {% load access_tags %}
   {% if_authorized comment.user object.user user %}
      <a href="{% url delete_comment comment.id %}">Delete</a>
   {% endif_authorized %}

The template tag file is called “access_tag.py” and is in my app’s “templatetags” directory. This is the contents of “access_tag.py”:

from django.template import Node, NodeList, TemplateSyntaxError
from django.template import Library, Variable, VariableDoesNotExist

register = Library()

def do_if_authorized(parser, token):
    """
    Outputs the contents of the block if the 'comment owner' or the 
    'page owner' is also the 'authenticated user'. As well, you can use
    an {% else %} tag to show text if the match fails.

    Takes three parameters:
      1) the comment owner
      2) the page owner
      3) the current authenticated user
    """
    bits = token.contents.split()
    if len(bits) != 4:
        raise TemplateSyntaxError("%s tag takes three arguments: \
                                   1) the comment owner \
                                   2) the page owner \
                                   3) the current authenticated user" % bits[0])
    nodelist_true = parser.parse(('else', 'endif_authorized'))
    token = parser.next_token()

    if token.contents == 'else':
        nodelist_false = parser.parse(('endif_authorized',))
        parser.delete_first_token()
    else:
        nodelist_false = NodeList()
    return IfAuthorizedNode(bits[1], bits[2], bits[3], nodelist_true, nodelist_false)

class IfAuthorizedNode(Node):
    def __init__(self, comment_owner, page_owner, authenticated_user, nodelist_true, nodelist_false):
        self.nodelist_true = nodelist_true
        self.nodelist_false = nodelist_false
        self.comment_owner = Variable(comment_owner)
        self.page_owner = Variable(page_owner)
        self.authenticated_user = Variable(authenticated_user)

    def render(self, context):
        try:
            comment_owner = self.comment_owner.resolve(context)
            page_owner = self.page_owner.resolve(context)
            authenticated_user = self.authenticated_user.resolve(context)
        except VariableDoesNotExist:
            return ''

        if comment_owner == authenticated_user or page_owner == authenticated_user:
            return self.nodelist_true.render(context)
        else:
            return self.nodelist_false.render(context)

register.tag('if_authorized', do_if_authorized)

Done. In the end, it would have been pretty easy to just use the built-in {% if %} tag to do this comparison, but since I’ll have other per-object authorizations to do, I will continue to build out these custom “access_tags”. Plus, the template code looks so much tidier 🙂

👤mitchf

2đź‘Ť

There already exists a project that aims to do what you would like to do.

django-authority allows for fine grain control over permissions in templates.

Django 1.2 also contains user permissions in templates, too.

👤Nick Presta

2đź‘Ť

how about this… create a custom tag that writes a variable in the context, then test that variable using {% if %}

it’d be something like this:

{% check_access comment %}
{% if has_access %}
    <a href="{% url delete_comment comment.id %}">Delete</a>
{% endif %}

of course the “check_access” tag would write the “has_access” in the context.

Good Luck

Leave a comment