Login

Generic views with row-level permission handling

Author:
mwicat
Posted:
May 4, 2011
Language:
Python
Version:
1.3
Tags:
generic-views permissions
Score:
2 (after 2 ratings)

These generic views extend default views so that they also do permission checking on per-object basis.

  • detail, update and delete - check access for user
  • create - create permissions for user on object
  • list - narrow object list with permissions

Classes prefixed with Owned are example implementation where user has access to object if designed object attribute references him.

Example:

create_article = OwnedCreateView.as_view(owner='creator', model=Article, form_class=ArticleForm, success_url='/articles/article/%(id)d')

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from django.views.generic.detail import DetailView
from django.views.generic.edit import UpdateView, CreateView, DeleteView
from django.views.generic.list import ListView
from django.core import exceptions
from django.core.exceptions import ImproperlyConfigured
from django.http import Http404, HttpResponseRedirect


class GuardedDetailView(DetailView):
    def get(self, request, **kwargs):
        self.object = self.get_object()
        self.check_permission(request.user, self.object)
        context = self.get_context_data(object=self.object)
        return self.render_to_response(context)


class GuardedUpdateView(UpdateView):
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.check_permission(request.user, self.object)
        return super(GuardedUpdateView, self).get(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.check_permission(request.user, self.object)
        return super(GuardedUpdateView, self).post(request, *args, **kwargs)


class GuardedCreateView(CreateView):
    def post(self, request, *args, **kwargs):
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        if form.is_valid():
            self.ensure_permissions(request.user, form.instance)
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

class GuardedDeleteView(DeleteView):
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.check_permission(request.user, self.object)
        return super(DeleteView, self).get(request, *args, **kwargs)

    def delete(self, request, *args, **kwargs):
        self.object = self.get_object()
        self.check_permission(request.user, self.object)
        self.object.delete()
        return HttpResponseRedirect(self.get_success_url())


class OwnedCreateView(GuardedCreateView):
    
    owner = None
    
    def ensure_permissions(self, user, object):
        setattr(object, self.owner, user)

    
class OwnedDetailView(GuardedDetailView):
    
    owner = None
    
    def check_permission(self, user, object):
        if getattr(object, self.owner) != user:
            raise exceptions.PermissionDenied()


class OwnedUpdateView(GuardedUpdateView):
    
    owner = None
    
    def check_permission(self, user, object):
        if getattr(object, self.owner) != user:
            raise exceptions.PermissionDenied()

class OwnedDeleteView(GuardedDeleteView):
    
    owner = None
    
    def check_permission(self, user, object):
        if getattr(object, self.owner) != user:
            raise exceptions.PermissionDenied()


class OwnedListView(ListView):
    
    owner = None
    
    def get_queryset_perm(self, user):
        """
        Get the list of items for this view. This must be an interable, and may
        be a queryset (in which qs-specific behavior will be enabled).
        """
        if self.queryset is not None:
            queryset = self.queryset
            if hasattr(queryset, '_clone'):
                queryset = queryset._clone()
        elif self.model is not None:
            lookup_args = { self.owner: user }
            queryset = self.model._default_manager.filter(**lookup_args)
        else:
            raise ImproperlyConfigured(u"'%s' must define 'queryset' or 'model'"
                                       % self.__class__.__name__)
        return queryset

    def get(self, request, *args, **kwargs):
        self.object_list = self.get_queryset_perm(request.user)
        allow_empty = self.get_allow_empty()
        if not allow_empty and len(self.object_list) == 0:
            raise Http404(_(u"Empty list and '%(class_name)s.allow_empty' is False.")
                          % {'class_name': self.__class__.__name__})
        context = self.get_context_data(object_list=self.object_list)
        return self.render_to_response(context)

More like this

  1. Class based generic views that automatically check permissions by humphreymurray 4 years, 7 months ago
  2. Link TemplateTag that checks for permissions and url address by mlouro 6 years, 3 months ago
  3. restrict user access to modeladmin via metaclass by code_shogan 4 years, 6 months ago
  4. models.py with django_dag models for parts hierarchy by j_syk 4 years ago
  5. View to retrieve objects meeting a complex tag query by nathangeffen 4 years, 8 months ago

Comments

bradbeattie (on June 15, 2012):

To get this working, I had to modify getattr(object, "owner") and lookup_args = { "owner": user }.

#

Please login first before commenting.