Django provides several ways to restrict access to views based on authentication status or permissions.
The login_required Decorator
Require users to be logged in:
pythonfrom django.contrib.auth.decorators import login_required @login_required def profile(request): return render(request, 'profile.html')
If not logged in, users are redirected to LOGIN_URL (default: /accounts/login/).
Custom Redirect
You can override the default login redirect URL and the query parameter name on a per-view basis.
python@login_required(login_url='/custom/login/') def profile(request): pass # Or set redirect field name @login_required(redirect_field_name='return_to') def profile(request): pass
Permission Required
Check for specific permissions:
pythonfrom django.contrib.auth.decorators import permission_required @permission_required('polls.add_question') def create_question(request): pass # Multiple permissions @permission_required(['polls.add_question', 'polls.change_question']) def manage_questions(request): pass # Raise 403 instead of redirecting @permission_required('polls.delete_question', raise_exception=True) def delete_question(request, pk): pass
User Passes Test
Custom logic for access control:
pythonfrom django.contrib.auth.decorators import user_passes_test def is_staff(user): return user.is_staff @user_passes_test(is_staff) def staff_dashboard(request): pass # Using lambda @user_passes_test(lambda u: u.is_superuser) def admin_only(request): pass
Checking in Templates
Django's template context includes the user and perms objects, letting you conditionally show content based on authentication and permissions.
html{% if user.is_authenticated %} <p>Welcome, {{ user.username }}!</p> <form method="post" action="{% url 'logout' %}"> {% csrf_token %} <button type="submit">Logout</button> </form> <!-- Note: Django 5.0+ requires POST for logout --> {% else %} <a href="{% url 'login' %}">Login</a> {% endif %} {% if user.is_staff %} <a href="{% url 'admin:index' %}">Admin</a> {% endif %} {% if perms.polls.add_question %} <a href="{% url 'create_question' %}">Create Question</a> {% endif %}
Checking in Views
You can also check authentication and permissions programmatically inside view functions for more complex access control logic.
pythondef my_view(request): # Check authentication if not request.user.is_authenticated: return redirect('login') # Check permissions if not request.user.has_perm('polls.change_question'): return HttpResponseForbidden("Permission denied") # Check multiple permissions if request.user.has_perms(['polls.add_question', 'polls.delete_question']): # User has both permissions pass return render(request, 'page.html')
LoginRequiredMixin for Class-Based Views
Decorators do not work directly on class-based views. Instead, use Django's authentication mixins as parent classes.
pythonfrom django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin from django.views.generic import ListView class QuestionListView(LoginRequiredMixin, ListView): model = Question login_url = '/login/' # Optional: override LOGIN_URL redirect_field_name = 'next' class QuestionCreateView(PermissionRequiredMixin, CreateView): model = Question permission_required = 'polls.add_question' # Or multiple: permission_required = ['polls.add_question', 'polls.view_question']
Creating a User Registration View
Django does not include a registration view out of the box, but you can build one using the built-in UserCreationForm and login() function.
pythonfrom django.contrib.auth.forms import UserCreationForm from django.contrib.auth import login def register(request): if request.method == 'POST': form = UserCreationForm(request.POST) if form.is_valid(): user = form.save() login(request, user) # Log in the new user return redirect('home') else: form = UserCreationForm() return render(request, 'registration/register.html', {'form': form})
html<!-- templates/registration/register.html --> <h1>Register</h1> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">Create Account</button> </form> <p>Already have an account? <a href="{% url 'login' %}">Login</a></p>
Common Pitfalls
- Using
@login_requiredon class-based views: Decorators do not work directly on classes. UseLoginRequiredMixininstead, placing it before the view class in the inheritance list. - Forgetting
raise_exception=Trueon@permission_required: Without it, unauthorized users are redirected to the login page instead of getting a 403 Forbidden response. - Checking permissions in templates without context processors: The
permsvariable requiresdjango.contrib.auth.context_processors.authin your template context processors.
Best Practices
- Use mixins for class-based views:
LoginRequiredMixinandPermissionRequiredMixinare the CBV equivalents of decorators. - Use
@user_passes_testfor custom logic: When you need more complex access control than login or permission checks. - Always redirect after login: Use the
nextparameter to send users back to the page they were trying to access.
Summary
@login_requiredredirects unauthenticated users toLOGIN_URLwith anextparameter@permission_requiredchecks for specific model-level permissions (e.g.,'app.add_model')@user_passes_testallows custom access control logic with a callable- For class-based views, use
LoginRequiredMixinandPermissionRequiredMixin - Check
user.is_authenticatedandperms.app.permissionin templates for conditional rendering
Code Examples
from django.contrib.auth.decorators import login_required, permission_required
@login_required
def profile(request):
return render(request, 'profile.html')
@permission_required('polls.add_question', raise_exception=True)
def create_question(request):
# Only users with 'polls.add_question' permission can access
pass