In most Django projects, not every page should be public. Some pages should only be visible to logged-in users, and some actions should only be allowed for specific users such as admins, editors, teachers, or managers.
For example:
This is where login protection and permissions become essential.
In this tutorial, you will learn how to:
By the end, you will know how to secure your Django pages properly and build applications with controlled access.
By the end of this tutorial, you will understand:
login_requiredpermission_requiredBefore starting, you should already know:
In a real application, some information should not be public.
Examples:
If you do not protect these pages, anyone can access them just by opening the URL.
So Django provides tools to control:
login_required — Protect Function-Based ViewsThe simplest way to protect a page is with login_required.
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
@login_required
def dashboard(request):
return render(request, 'dashboard.html')Django needs to know where to send unauthenticated users.
settings.pyLOGIN_URL = 'login'You can also use a direct path:
LOGIN_URL = '/login/'If a user tries to open a protected page while not logged in, Django redirects them there.
You can also define where users go after logging in.
settings.pyLOGIN_REDIRECT_URL = 'dashboard'
LOGOUT_REDIRECT_URL = 'login'This means:
urls.pyfrom django.urls import path
from .views import dashboard
urlpatterns = [
path('dashboard/', dashboard, name='dashboard'),
]Now dashboard/ is protected.
Suppose a visitor tries to access:
/dashboard/If they are not logged in, Django redirects them to:
/login/?next=/dashboard/The next parameter tells Django where to return the user after successful login.
This is very useful because the user returns to the page they originally wanted.
For class-based views, Django uses LoginRequiredMixin.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
class DashboardView(LoginRequiredMixin, TemplateView):
template_name = 'dashboard.html'Put LoginRequiredMixin before the main view class.
Correct:
class DashboardView(LoginRequiredMixin, TemplateView):Not:
class DashboardView(TemplateView, LoginRequiredMixin):You can also protect generic views the same way.
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView
from .models import Post
class PostListView(LoginRequiredMixin, ListView):
model = Post
template_name = 'blog/post_list.html'
context_object_name = 'posts'Now only logged-in users can see the post list.
Some pages should only be visible to staff users.
Django’s User model has built-in fields:
is_authenticatedis_staffis_superuserfrom django.contrib.auth.decorators import user_passes_test
from django.shortcuts import render
def is_staff_user(user):
return user.is_staff
@user_passes_test(is_staff_user)
def staff_dashboard(request):
return render(request, 'staff_dashboard.html')This allows only staff users.
user_passes_testuser_passes_test lets you define your own rule.
from django.contrib.auth.decorators import user_passes_test
def is_admin(user):
return user.is_superuser
@user_passes_test(is_admin)
def admin_panel(request):
return render(request, 'admin_panel.html')Django automatically creates permissions for each model:
add_modelnamechange_modelnamedelete_modelnameview_modelnameFor example, for a Post model in an app called blog, Django creates permissions like:
blog.add_postblog.change_postblog.delete_postblog.view_postThese permissions can be assigned to users or groups.
permission_requiredDjango provides the permission_required decorator.
from django.contrib.auth.decorators import permission_required
@permission_required('blog.add_post')
def add_post(request):
return render(request, 'blog/add_post.html')This means only users with the permission blog.add_post can access the page.
If they do not have permission, access is denied.
permission_requiredBy default, permission_required redirects to login if the user is not logged in.
You can also raise an error instead:
@permission_required('blog.add_post', raise_exception=True)
def add_post(request):
return render(request, 'blog/add_post.html')If the user lacks permission, Django returns a 403 Forbidden error.
Sometimes you want more custom control.
from django.http import HttpResponseForbidden
from django.shortcuts import render
def add_post(request):
if not request.user.has_perm('blog.add_post'):
return HttpResponseForbidden("You do not have permission to add posts.")
return render(request, 'blog/add_post.html')
This is useful when you want custom messages or custom logic.
For CBVs, use PermissionRequiredMixin.
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views.generic import TemplateView
class AddPostView(PermissionRequiredMixin, TemplateView):
template_name = 'blog/add_post.html'
permission_required = 'blog.add_post'Only users with that permission can access the page.
Often you want both login and permission protection.
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views.generic import TemplateView
class AddPostView(LoginRequiredMixin, PermissionRequiredMixin, TemplateView):
template_name = 'blog/add_post.html'
permission_required = 'blog.add_post'This means:
blog.add_post permissionDjango groups let you assign permissions to several users at once.
For example, you may create groups like:
Instead of assigning permissions user by user, you assign permissions to the group, then add users to the group.
Go to Django admin:
Editorsblog.add_postblog.change_postblog.delete_postNow all users in the Editors group inherit those permissions.
This is much easier for real projects.
You can check whether a user belongs to a group.
def is_editor(user):
return user.groups.filter(name='Editors').exists()Then use it with user_passes_test:
from django.contrib.auth.decorators import user_passes_test
@user_passes_test(is_editor)
def editor_dashboard(request):
return render(request, 'editor_dashboard.html')Django lets you check permissions directly in templates.
{% if perms.blog.add_post %}
<a href="{% url 'add_post' %}">Add Post</a>
{% endif %}This means:
blog.add_postYou can also check authentication:
{% if user.is_authenticated %}
<a href="{% url 'dashboard' %}">Dashboard</a>
{% endif %}This is important:
For example, even if a user does not see the “Delete Post” button, they could still manually type the delete URL into the browser.
So always protect both:
Sometimes the rule is not based on staff or permissions, but ownership.
For example:
from django.http import HttpResponseForbidden
from django.shortcuts import get_object_or_404, render
from .models import Post
def edit_post(request, post_id):
post = get_object_or_404(Post, id=post_id)
if request.user != post.author:
return HttpResponseForbidden("You can only edit your own posts.")
return render(request, 'blog/edit_post.html', {'post': post})For CBVs, you can override dispatch() or get_object().
from django.http import HttpResponseForbidden
from django.views.generic import UpdateView
from .models import Post
class PostUpdateView(UpdateView):
model = Post
fields = ['title', 'content']
template_name = 'blog/post_form.html'
def dispatch(self, request, *args, **kwargs):
post = self.get_object()
if request.user != post.author:
return HttpResponseForbidden("You can only edit your own posts.")
return super().dispatch(request, *args, **kwargs)This protects the update page so only the author can edit the post.
Let us imagine a blog with these rules:
views.pyfrom django.contrib.auth.decorators import login_required, permission_required
from django.http import HttpResponseForbidden
from django.shortcuts import render, get_object_or_404
from .models import Post
@login_required
def dashboard(request):
return render(request, 'dashboard.html')
@permission_required('blog.add_post', raise_exception=True)
def add_post(request):
return render(request, 'blog/add_post.html')
@login_required
def edit_post(request, post_id):
post = get_object_or_404(Post, id=post_id)
if request.user != post.author:
return HttpResponseForbidden("You can only edit your own posts.")
return render(request, 'blog/edit_post.html', {'post': post})This is a realistic access-control structure.
You can define your own permissions inside a model.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
class Meta:
permissions = [
('can_publish_post', 'Can publish post'),
]After migrations, Django creates this custom permission.
You can assign it to users or groups just like built-in permissions.
Then use it like this:
@permission_required('blog.can_publish_post', raise_exception=True)
def publish_post(request):
...Showing or hiding links in templates is not enough.
login_required but not setting LOGIN_URLThis may cause redirect confusion.
Being logged in does not automatically mean the user has permission.
For bigger projects, groups make permission management much easier.
A logged-in user should not automatically be able to edit everyone’s content.
login_requiredPermissionRequiredMixin or permission_required for restricted actionsImagine a school platform:
Possible rules:
login_required for student dashboardpermission_required('school.add_grade') for teachersManagers for reportsThis is exactly the kind of access control real Django applications need.
In this tutorial, you learned that:
login_required protects pages for logged-in usersLoginRequiredMixin protects class-based viewspermission_required and PermissionRequiredMixin restrict access by permissionA. user_passes_test
B. permission_required
C. login_required
D. auth_required
A. UserRequiredMixin
B. LoginRequiredMixin
C. AuthMixin
D. SecureViewMixin
A. group_required
B. login_required
C. permission_required
D. staff_required
user.has_perm() check?A. Whether the user is active
B. Whether the user has a specific permission
C. Whether the user belongs to a group
D. Whether the user is logged out
A. They replace models
B. They speed up templates
C. They let you assign permissions to multiple users easily
D. They automatically create dashboards
The next ideal tutorial is:
Tutorial: Django Sessions and Cookies
Subject: how Django remembers users, stores session data, and manages cookies.