Skip to content

Decorators

permission_required(perm, lookup_variables=None, **kwargs)

A Django view decorator that checks whether a user has a particular permission enabled.

Optionally, instances for which check should be made may be passed as a second argument or as a tuple parameters same as those passed to get_object_or_404 but must be provided as pairs of strings. This way decorator can fetch i.e. User instance based on performed request and check permissions on it (without this, one would need to fetch user instance at view's logic and check permission inside a view).

Parameters:

Name Type Description Default
perm str

permission to check in format: 'app_label.codename'.

required
lookup_variables tuple

optional, instances for which check should be made.

None

Other Parameters:

Name Type Description
login_url str

if denied, user would be redirected to location set by this parameter. Defaults to django.conf.settings.LOGIN_URL.

redirect_field_name str

name of the parameter passed if redirected. Defaults to django.contrib.auth.REDIRECT_FIELD_NAME.

return_403 bool

if True then instead of redirecting to the login page, a response with status code 403 is returned (django.http.HttpResponseForbidden instance or rendered template - see GUARDIAN_RENDER_403). Defaults to False.

return_404 boot

if True then instead of redirecting to the login page, a response with status code 404 is returned (django.http.HttpResponseNotFound instance or rendered template - see GUARDIAN_RENDER_404). Defaults to False.

accept_global_perms bool

if set to True, then object level permission would be required only if user does NOT have global permission for target model. If turned on, makes this decorator like an extension over standard django.contrib.admin.decorators.permission_required as it would check for global permissions first. Defaults to False.

Example
@permission_required('auth.change_user', return_403=True)
def my_view(request):
    return HttpResponse('Hello')

@permission_required('auth.change_user', (User, 'username', 'username'))
def my_view(request, username):
    '''
    auth.change_user permission would be checked based on given
    'username'. If view's parameter would be named `name`, we would
    rather use following decorator::

        @permission_required('auth.change_user', (User, 'username', 'name'))
    '''
    user = get_object_or_404(User, username=username)
    return user.get_absolute_url()

@permission_required('auth.change_user',
    (User, 'username', 'username', 'groups__name', 'group_name'))
def my_view(request, username, group_name):
    '''
    Similar to the above example, here however we also make sure that
    one of user's group is named same as request's `group_name` param.
    '''
    user = get_object_or_404(User, username=username,
        group__name=group_name)
    return user.get_absolute_url()
Source code in guardian/decorators.py
 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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
def permission_required(perm, lookup_variables=None, **kwargs):
    """A Django view decorator that checks whether a user has a particular permission enabled.

    Optionally, instances for which check should be made may be passed as a
    second argument or as a tuple parameters same as those passed to
    `get_object_or_404` but must be provided as pairs of strings. This way
    decorator can fetch i.e. `User` instance based on performed request and
    check permissions on it (without this, one would need to fetch user instance
    at view's logic and check permission inside a view).

    Parameters:
        perm (str): permission to check in format: 'app_label.codename'.
        lookup_variables (tuple): optional, instances for which check should be made.

    Other Parameters:
        login_url (str): if denied, user would be redirected to location set by
            this parameter. Defaults to `django.conf.settings.LOGIN_URL`.
        redirect_field_name (str): name of the parameter passed if redirected.
            Defaults to `django.contrib.auth.REDIRECT_FIELD_NAME`.
        return_403 (bool): if `True` then instead of redirecting to the
            login page, a response with status code 403 is returned
            (`django.http.HttpResponseForbidden` instance or rendered template -
            see `GUARDIAN_RENDER_403`). Defaults to `False`.
        return_404 (boot): if `True` then instead of redirecting to the
            login page, a response with status code 404 is returned
            (`django.http.HttpResponseNotFound` instance or rendered template -
            see `GUARDIAN_RENDER_404`). Defaults to `False`.
        accept_global_perms (bool): if set to `True`, then *object level
            permission* would be required **only if user does NOT have global
            permission** for target *model*. If turned on, makes this decorator
            like an extension over standard
            `django.contrib.admin.decorators.permission_required` as it would
            check for global permissions first. Defaults to `False`.

    Example:
        ```python
        @permission_required('auth.change_user', return_403=True)
        def my_view(request):
            return HttpResponse('Hello')

        @permission_required('auth.change_user', (User, 'username', 'username'))
        def my_view(request, username):
            '''
            auth.change_user permission would be checked based on given
            'username'. If view's parameter would be named `name`, we would
            rather use following decorator::

                @permission_required('auth.change_user', (User, 'username', 'name'))
            '''
            user = get_object_or_404(User, username=username)
            return user.get_absolute_url()

        @permission_required('auth.change_user',
            (User, 'username', 'username', 'groups__name', 'group_name'))
        def my_view(request, username, group_name):
            '''
            Similar to the above example, here however we also make sure that
            one of user's group is named same as request's `group_name` param.
            '''
            user = get_object_or_404(User, username=username,
                group__name=group_name)
            return user.get_absolute_url()
        ```

    """
    login_url = kwargs.pop("login_url", settings.LOGIN_URL)
    redirect_field_name = kwargs.pop("redirect_field_name", REDIRECT_FIELD_NAME)
    return_403 = kwargs.pop("return_403", False)
    return_404 = kwargs.pop("return_404", False)
    accept_global_perms = kwargs.pop("accept_global_perms", False)

    # Check if perm is given as string in order not to decorate
    # view function itself which makes debugging harder
    if not isinstance(perm, str):
        raise GuardianError(
            "First argument must be in format: 'app_label.codename or a callable which return similar string'"
        )

    def decorator(view_func):
        def _wrapped_view(request, *args, **kwargs):
            # if more than one parameter is passed to the decorator we try to
            # fetch object for which check would be made
            obj = None
            if lookup_variables:
                model, lookups = lookup_variables[0], lookup_variables[1:]
                # Parse model
                if isinstance(model, str):
                    splitted = model.split(".")
                    if len(splitted) != 2:
                        raise GuardianError(
                            "If model should be looked up from string it needs format: 'app_label.ModelClass'"
                        )
                    model = apps.get_model(*splitted)
                elif issubclass(model.__class__, (Model, ModelBase, QuerySet)):
                    pass
                else:
                    raise GuardianError(
                        "First lookup argument must always be "
                        "a model, string pointing at app/model or queryset. "
                        "Given: %s (type: %s)" % (model, type(model))
                    )
                # Parse lookups
                if len(lookups) % 2 != 0:
                    raise GuardianError("Lookup variables must be provided as pairs of lookup_string and view_arg")
                lookup_dict = {}
                for lookup, view_arg in zip(lookups[::2], lookups[1::2]):
                    if view_arg not in kwargs:
                        raise GuardianError("Argument %s was not passed into view function" % view_arg)
                    lookup_dict[lookup] = kwargs[view_arg]
                obj = get_object_or_404(model, **lookup_dict)

            response = get_40x_or_None(
                request,
                perms=[perm],
                obj=obj,
                login_url=login_url,
                redirect_field_name=redirect_field_name,
                return_403=return_403,
                return_404=return_404,
                accept_global_perms=accept_global_perms,
            )
            if response:
                return response
            return view_func(request, *args, **kwargs)

        return wraps(view_func)(_wrapped_view)

    return decorator

permission_required_or_403(perm, *args, **kwargs)

Check if user has permission, if not return 403.

This decorator is wrapper for permission_required decorator. The only difference between permission_required decorator is that this one always set return_403 parameter to True.

Note

The standard Django's permission_required decorator redirects user to login page when a permission check failed. This decorator may be used to return HttpResponseForbidden (status 403) instead of redirection.

Source code in guardian/decorators.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def permission_required_or_403(perm, *args, **kwargs):
    """Check if user has permission, if not return 403.

    This decorator is wrapper for `permission_required` decorator.
    The only difference between `permission_required` decorator is that this
    one always set `return_403` parameter to `True`.

    Note:
        The standard Django's `permission_required` decorator redirects user to login page
        when a permission check failed.
        This decorator may be used to return HttpResponseForbidden (status 403)
        instead of redirection.
    """
    kwargs["return_403"] = True
    return permission_required(perm, *args, **kwargs)

permission_required_or_404(perm, *args, **kwargs)

Check if user has permission, if not return 404.

This decorator is wrapper for permission_required decorator. The only difference between permission_required decorator is that this one always set return_404 parameter to True.

Note

The standard Django's permission_required decorator redirects users to the login page when a permission check fails. This decorator may be used to return HttpResponseNotFound (status 404) instead of redirection.

Source code in guardian/decorators.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
def permission_required_or_404(perm, *args, **kwargs):
    """Check if user has permission, if not return 404.

    This decorator is wrapper for permission_required decorator.
    The only difference between `permission_required` decorator is that this
    one always set `return_404` parameter to `True`.

    Note:
        The standard Django's `permission_required` decorator redirects users to the login page
        when a permission check fails.
        This decorator may be used to return HttpResponseNotFound (status 404)
        instead of redirection.
    """
    kwargs["return_404"] = True
    return permission_required(perm, *args, **kwargs)