← Back to Django Course | Chapter 10: Authentication & Authorization | Lesson 7 of 12

The permission_required Decorator

permission_required is like a VIP wristband check — it only lets in people who have been granted that specific permission.

Requiring a Specific Permission

@permission_required takes a permission string in the form 'app_label.action_modelname', such as 'library.add_book'.

Example: Requiring a Specific Permission

Only users (or groups) granted the 'library.add_book' permission can reach this view; others get redirected.

markup
from django.contrib.auth.decorators import permission_required
from django.shortcuts import render

@permission_required('library.add_book')
def add_book_view(request):
    return render(request, 'add_book.html')
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

Returning 403 Instead of Redirecting

By default a denied user is redirected to LOGIN_URL. Setting raise_exception=True returns an HTTP 403 error page instead.

Example: Returning 403 Instead of Redirecting

raise_exception=True is useful when the user IS logged in but simply lacks the permission — a redirect to login would be misleading.

markup
from django.contrib.auth.decorators import permission_required

@permission_required('library.delete_book', raise_exception=True)
def delete_book_view(request):
    return delete_book(request)
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}

Checking Permissions in Templates

Templates can hide or show links based on request.user.has_perm, matching the same permission string.

Example: Checking Permissions in Templates

has_perm() mirrors the decorator's check, so the link only appears for users who could actually use it.

markup
{% if user.has_perm('library.add_book') %}
  <a href="{% url 'add_book' %}">Add Book</a>
{% endif %}
{# Django-only code -- models.py/views.py/urls.py/settings.py snippets, or template markup using Django template tags/variables -- can't run standalone via Judge0 or the browser preview, since it needs a real Django project. Only this course's pure-Python examples (example_lang == 'python', no Django imports) are actually runnable, so those still get the button below. #}
Common Mistakes
  1. Forgetting the app_label prefix in the permission string, e.g. writing add_book instead of 'library.add_book'.
  2. Assuming permission_required also checks login — it does, but only redirects, it doesn't tell the user WHY access was denied unless raise_exception is set.
  3. Granting is_superuser instead of the specific permission when only one narrow permission was actually needed.
Chapter Summary
  • @permission_required checks a specific permission string like 'app_label.action_modelname'.
  • By default it redirects unauthenticated or unauthorized users, just like login_required.
  • raise_exception=True makes it return a 403 Forbidden instead of redirecting.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.