When most pages in a site require authentication, decorating all the views with
@login_required can be annoying. You can reverse the default behavior by
creating a custom middleware class:
import urllib from django.conf import settings from django.contrib.auth import REDIRECT_FIELD_NAME from django.http import HttpResponseRedirect def allow_anonymous(view_func): view_func.allow_anonymous = True return view_func class RequireLogin: def process_view(self, request, view_func, view_args, view_kwargs): if request.path != settings.LOGIN_URL and not request.user.is_authenticated() and not getattr(view_func, 'allow_anonymous', False): url = '%s?%s=%s' % (settings.LOGIN_URL, REDIRECT_FIELD_NAME, urllib.quote(request.get_full_path())) return HttpResponseRedirect(url)
That’s an ugly block of code but it’s not too complex.
allow_anonymous is a
function decorator, like
login_required. It just tags the function to tell the
middleware that authentication isn’t required. The
RequireLogin class verifies
that the user is logged in. If not, and if the function is not decorated with
allow_anonymous, it redirects to
So put that code in a file in your project – let’s say,
yourproject/yourapp/middleware.py. Then open
settings.py and add
MIDDLEWARE_CLASSES. This tells Django about your new middleware class, and
RequireLogin.process_view will be called any time a view is about to be
Now you can use it in your view:
from yourproject.yourapp.middleware import allow_anonymous def some_private_view(request): # won't be accessible unless user is logged in return HttpResponse('Hello, user!') @allow_anonymous def some_public_view(request): return HttpResponse('Hello, world!')
If all is working correctly,
some_private_view should ask for a login, but
some_public_view will allow viewing without it.
Update: I’ve updated the code to fix a bug when using
django.contrib.auth.views.login for your login view. As you can’t mark this
view function with
@allow_anonymous, it would infinitely redirect back to it.
Oops! Thanks for pointing it out, Phil.