From 69d169f5f629c1d02361198c4a76839a9f8d528d Mon Sep 17 00:00:00 2001 From: Tom Christie Date: Fri, 8 Mar 2013 23:42:20 +0000 Subject: [PATCH] Neater override hooks and more docs for DjangoModelPermissions. Refs #702. --- docs/api-guide/permissions.md | 7 ++++++- rest_framework/permissions.py | 13 ++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/api-guide/permissions.md b/docs/api-guide/permissions.md index 2db6ce1e3..719ac1eff 100644 --- a/docs/api-guide/permissions.md +++ b/docs/api-guide/permissions.md @@ -90,12 +90,17 @@ This permission is suitable if you want to your API to allow read permissions to ## DjangoModelPermissions -This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. When applied to a view that has a `.model` property, authorization will only be granted if the user has the relevant model permissions assigned. +This permission class ties into Django's standard `django.contrib.auth` [model permissions][contribauth]. When applied to a view that has a `.model` property, authorization will only be granted if the user *is authenticated* and has the *relevant model permissions* assigned. * `POST` requests require the user to have the `add` permission on the model. * `PUT` and `PATCH` requests require the user to have the `change` permission on the model. * `DELETE` requests require the user to have the `delete` permission on the model. +If you want to use `DjangoModelPermissions` but also allow unauthenticated users to have read permission, override the class and set the `authenticated_users_only` property to `False`. For example: + + class HasModelPermissionsOrReadOnly(DjangoModelPermissions): + authenticated_users_only = False + The default behaviour can also be overridden to support custom model permissions. For example, you might want to include a `view` model permission for `GET` requests. To use custom model permissions, override `DjangoModelPermissions` and set the `.perms_map` property. Refer to the source code for details. diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py index 306f00ca2..f18fb53e2 100644 --- a/rest_framework/permissions.py +++ b/rest_framework/permissions.py @@ -102,6 +102,8 @@ class DjangoModelPermissions(BasePermission): 'DELETE': ['%(app_label)s.delete_%(model_name)s'], } + authenticated_users_only = True + def get_required_permissions(self, method, model_cls): """ Given a model and an HTTP method, return the list of permission @@ -115,13 +117,18 @@ class DjangoModelPermissions(BasePermission): def has_permission(self, request, view): model_cls = getattr(view, 'model', None) - if not model_cls: - return True + queryset = getattr(view, 'queryset', None) + + if model_cls is None and queryset is not None: + model_cls = queryset.model + + assert model_cls, ('Cannot apply DjangoModelPermissions on a view that' + ' does not have `.model` or `.queryset` property.') perms = self.get_required_permissions(request.method, model_cls) if (request.user and - request.user.is_authenticated() and + (request.user.is_authenticated() or not self.authenticated_users_only) and request.user.has_perms(perms)): return True return False