Using Django Permissions

I recently had the need to use the Django permission system and couple it with newforms admin. There were a couple of tricks in the process so hopefully this will be useful for you.

The scenario, is that I want to have certain users identified as members and then use this permission to control access to other parts of the site. Of course, you can go into the user admin and add the permission bits but it would be better if I could integrate it with the other form that adds additional information to the user.

First, let's setup our model. Here's the stripped down models.py file:

from django.db import models
from django.contrib.auth.models import User

class Member(models.Model):
    """
    The basic member model.
    Brings together all the various models into one place.
    """
    user = models.ForeignKey(User, unique=True)
    date_joined = models.DateField()
    nick_name = models.CharField(max_length=30)
    active = models.BooleanField(help_text = "Is this membership active?")
    # Put any other info here

    class Meta:
        permissions = (
            ("is_member", "Active Member"),
        )

The only unique thing about this model is the permissions variable in Meta. All this does is create a new permission called "is_member" whenever you sync the database. It's not terribly useful at this point.

What we'd like to do is make sure that toggling the "active" variable will automatically assign or reassign the permission bit to the corresponding user. We do this in the admin.py file:

from django.contrib.auth.models import User
from django.contrib import admin
from django.contrib.auth.models import Permission

class MemberAdmin(admin.ModelAdmin):
    """
    When we save a member, we need to make sure that the
    appropriate permissions are added.

    Then, when we delete, we need to make sure they are removed.
    """

    def save_model(self, request, obj, form, change):
        """
        We use this form to make sure the permissions are up to date
        and that we store the appropriate info into the user fields too.
        """
        user_id = request.REQUEST.get('user',None)
        user = User.objects.get(id=user_id)
        member_status = request.REQUEST.get('active',False)
        members_only = Permission.objects.get(codename="is_member")
        if user and member_status:
            user.user_permissions.add(members_only)
        elif user:
            user.user_permissions.remove(members_only)
        obj.save()

    def log_deletion(self, request, object, object_repr):
        members_only = Permission.objects.get(codename="is_member")
        if object.user:
            object.user.user_permissions.remove(members_only)
        super(MemberAdmin, self).log_deletion(request, object, object_repr)

admin.site.register(Member,MemberAdmin)

This is where the more interesting parts happen. First, we override save_model and determine the user attached to the member and add the appropriate permission. The key to adding the permission is that we need to get the permission using:

members_only = Permission.objects.get(codename="is_member")

Then, you can add or delete the permission using:

user.user_permissions.add(members_only)
user.user_permissions.remove(members_only)

The final piece is making sure that you remove the permission when a user is deleted. We have to override the log_deletion method to know when an object is deleted. Then we just remove the permission.

The really nice thing is that now we can use the permission anywhere we need to and there's no need to write any special permission handling code in your views or templates.

Enjoy!

Posted on November 28, 2008 by chris django

7 Comments

Eric Florenzano commented on November 28, 2008 at 2:10 p.m.:
Great tutorial, but why not simply override the save() and delete() methods on the model instead of doing all the heavy lifting inside of the Admin?

Reply
chris commented on November 28, 2008 at 3:45 p.m.:
Eric - Good question. If I recall correctly, the problem is that the save method does not have access to the user at the time it's called. That's why I did it in the admin using REQUEST to get the user id and pulling the user. -Chris

Reply
krylatij commented on December 4, 2008 at 8:23 a.m.:
>>..is that the save method does not have access to the user.. You can use threadlocals middleware for this

Reply
Chris commented on December 7, 2008 at 8:28 a.m.:
Yes, threadlocals is a similar approach but my understanding is that threadlocals is a bit hackier.

Reply
apollo13 commented on June 22, 2009 at 10:57 a.m.:
save_model passes you the form, so why not grep the data from form.cleaned_data instead of request.REQUEST? And the save method should have access to the user, just use self.user and you get the user-instance…

Reply
jessy commented on August 20, 2009 at 4:31 p.m.:

wow, thanks-- the user authentication docs dont document the fact that user.user_permissions.add() takes a permission object as argument, and not a string. even better, user.user_permissions.add("random string") doesnt throw an error. super :).

thanks for the post, it ended a confused half hour search!



Reply
Carlos commented on June 14, 2011 at 11:16 p.m.:
Thanks for the post. I wasted 1 hr trying to figure this out until I found this blog post. I am really starting to second guess Django advantages to save time if I have to spend all this time getting basic doc on how use their apis. They need better doc. Coming from Java I am really missing Java Docs. Which is what Django needs.

Reply

Reply to original: