Many views in a Django project — the profile page, the cart, the dashboard — should only open for logged-in users, but writing if not request.user.is_authenticated by hand at the top of every view is repetitive and easy to forget. Django already has the answer: the @login_required decorator for function views and the LoginRequiredMixin mixin for class-based views. Both redirect anonymous visitors to the login page and bring them back to the exact page they wanted after signing in. Use them from the start for every view that needs authentication, instead of checking manually inside each function.

The Problem

Suppose you have a members-only profile page. The hand-written approach is to check request.user.is_authenticated inside each view and redirect to the login page when the visitor is anonymous. That is fine for one view, but when a project has dozens of views to protect, the check repeats everywhere, and forgetting it in one place leaks data. Worse, if you write the redirect yourself and forget to keep the destination address, users land on the home page after logging in instead of the page they were trying to see.

# views.py — the hand-written way, repeated in every view
from django.shortcuts import redirect, render
def profile(request):
if not request.user.is_authenticated:
return redirect("login")
return render(request, "profile.html", {"user": request.user})

The Solution

The @login_required decorator does exactly that in one line: anonymous visitors are sent to the login page, with a next parameter holding the original address so Django returns them there after login. For class-based views, use LoginRequiredMixin placed first in the inheritance list — that order is mandatory, because the mixin must run first. If your login page is not at the default /accounts/login/, just declare LOGIN_URL in settings or pass login_url to the decorator.

# views.py
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import render
from django.views.generic import DetailView
from .models import Order
@login_required
def profile(request):
return render(request, "profile.html", {"user": request.user})
class OrderDetailView(LoginRequiredMixin, DetailView):
model = Order
template_name = "order_detail.html"
# settings.py
LOGIN_URL = "login" # name of the login page URL pattern
LOGIN_REDIRECT_URL = "home" # default page after login

Apply It to Your Project

Review every view in your project and attach @login_required or LoginRequiredMixin to each one showing private data — profile, orders, account settings, internal admin pages. For APIs using session authentication, the same decorator still works; for token-based APIs, switch to DRF permissions. One small note: do not attach the decorator to the login or registration page, or anonymous visitors will be redirected in a circle and never get in.

Related Posts

Frequently Asked Questions

Where are anonymous visitors sent?

To the page in LOGIN_URL, by default /accounts/login/, with a ?next=/original/path parameter so Django sends them back to the exact page they wanted after they log in.

Where must LoginRequiredMixin go in the class?

First in the inheritance list, before View, DetailView, or ListView. Placed after them, the mixin does not run and the view stays open to anonymous visitors.

What if a page should stay public but hide buttons from guests?

Do not use the decorator. In the template, check {% if user.is_authenticated %} to show or hide individual parts — for example, the “Place order” button only appears for members.

Can I combine it with role-based permissions?

Yes. Combine LoginRequiredMixin with UserPassesTestMixin or PermissionRequiredMixin, and always keep the authentication mixin before the permission mixin in the inheritance list.

💬 Have you tried this in your own project? If you hit an error or know a shorter way to do it, leave a comment below — I read them all and will add the best ideas to the post.

References