开发者

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

I'm trying, in vain, to create a simple Django template tag to either show or hide a "delete" link next to a submitted comment on my site.

In a nutshell, I want to pass the comment object to the template tag, determine if the currently logged in user is authorized to delete the comment and then either show or not show the link.

The usage in my template would be like so:

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

Rest assured that I also check in the appropriate view if the user is authorized to delete the comment.

Does this type of tag have a specific name? It would certainly help me with my Google searches if it did. Thanks for your help!

UPDATE 1:

The way my site works, two people are potentially authorized to delete a comment: 1) the comment creator and 2) the owner of the post where the comment was left. Because of this, I need to determine, per comment, if one of those conditions is present.

I don't think I can use something like Django's built-in permission sytem, since it requires that permissions "be set globally per type of o开发者_开发知识库bject, no per specific object instance".

In my case, user "Bob" may have permissions to delete a comment (if he wrote it or it is on a post he created), but he also may not be allowed to delete it (if he is looking at a comment on someone else's post).

UPDATE 2:

It appears that you can't pass objects to a template tag, only strings: "Although you can pass any number of arguments to a template tag using token.split_contents(), the arguments are all unpacked as string literals." I guess I'll pass the id of the comment object in question and pull it in the tag.

I was wrong about this, just have to access the passed in object like:

self.comment.resolve(context).user 

vs.

self.comment.user


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


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.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜