Private by default
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 settings.LOGIN_URL
.
So put that code in a file in your project – let’s say,
yourproject/yourapp/middleware.py
. Then open settings.py
and add
"yourproject.yourapp.middleware.RequireLogin"
to
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
rendered.
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.