from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
@login_required
def request_loan(request, pk):
...
class MyActiveLoansView(LoginRequiredMixin, ListView):
model = Loan
login_url = "/accounts/login/"
# inside any view, once AuthenticationMiddleware has run:
request.user # a real User, or an AnonymousUser instance — never None
request.user.is_authenticated # True/False — safe to check even when nobody's logged in
request.user.has_perm("loans.mark_returned") # a custom permission, checked here
A visitor who's never logged in still gets a real object sitting behind request.user — an AnonymousUser instance, standing in so the attribute is never simply absent. That's worth knowing specifically because it means request.user is None can never catch a logged-out visitor; it'll always evaluate False, logged in or not, since there's always something there. .is_authenticated is the attribute actually built to answer the question. Four permissions get generated automatically for every model — add_, change_, delete_, and view_, each one prefixed onto the model's name — and has_perm checks a user against those plus anything a model's own Meta.permissions defines on top, like the custom loans.mark_returned that decides who's allowed to close out a loan that isn't theirs. A Group bundles a set of permissions under one name so they can be handed to a whole batch of users in one assignment instead of one at a time.