35

I have a django website with many urls and views. Now I have asked to redirect all non-authenticated users to a certain landing page. So, all views must check if user.is_authenticated() and return to a new set of landing pages.

Can it be done in a pretty way, instead of messing with my views.py/urls.py that much?

xpanta
  • 8,124
  • 15
  • 60
  • 104

9 Answers9

71

There is a simpler way to do this, just add the "login_url" parameter to @login_required and if the user is not login he will be redirected to the login page. You can find it here

from django.contrib.auth.decorators import login_required

@login_required(login_url='/accounts/login/')
def my_view(request):
    ...
AriG
  • 841
  • 1
  • 6
  • 6
  • 2
    The elegant solution perfect and works like a charm. Have added the login and logout urls to my login/logout urls and they inturn assist with perfect redirection. Thanks for sharing. – Doogle Sep 27 '18 at 15:13
  • It can't work for a class ? I have created a class that inherits from Django LoginView `CustomLoginView(LoginView)` However I have this error message : `path('login/', views.CustomLoginView.as_view(), name='login')` – Solal Mar 14 '19 at 09:50
  • 3
    @Solal Use `LoginRequiredMixin` for example `class someView(LoginRequiredMixin, CreateView):` etc. In the body don't forget to add the `login_url` to redirect the page to. – Love Putin Not War Jul 28 '20 at 03:01
  • How to do this for non-group users if their group doesn't allow it. – AnonymousUser Jul 18 '21 at 04:14
23

You can use Middleware.

Something like this will check user auth every request:

class AuthRequiredMiddleware(object):
    def process_request(self, request):
        if not request.user.is_authenticated():
            return HttpResponseRedirect(reverse('landing_page')) # or http response
        return None

Docs: process_request

Also, don't forget to enable it in settings.py

MIDDLEWARE_CLASSES = (
    ...
    'path.to.your.AuthRequiredMiddleware',
)
trinaldi
  • 2,872
  • 2
  • 32
  • 37
Dmit3Y
  • 496
  • 2
  • 5
  • 7
    This answer is somewhat outdated. For current versions of Django, best to see @AriG's answer below. – Craig S. Anderson Nov 29 '18 at 00:45
  • 5
    But that solution requires specifying the login url for every single view. The OP says that he wants this to happen for all unauthed requests to all views. – c6754 Jan 24 '20 at 20:52
16

see the docs for login required decorator

from django.contrib.auth.decorators import login_required

@login_required
def my_view(request):
    ...

another option is to add it to your urls.py patterns, see this answer

urlpatterns = patterns('',
    (r'^foo/$', login_required(direct_to_template), {'template': 'foo_index.html'}),
)
Community
  • 1
  • 1
Guy Gavriely
  • 11,228
  • 6
  • 27
  • 42
  • 2
    This is what I would like to avoid. Adding decorators to all my views and changing all my url actions. On the other hand I don't want to redirect to login, but to another page. From that page if user wants may go to login to leave. – xpanta Jan 15 '14 at 05:33
  • although this doesnt address the OP question, it still gives a nice solution. I would add that your decorator can have a redirect; example @login_required(login_url='/accounts/signin') – adhg Oct 19 '18 at 19:19
14

As of Django 1.10, the custom middleware classes must implement the new style syntax. You can use the following class to verify that the user is logged in while trying to access any views.

from django.shortcuts import HttpResponseRedirect


class AuthRequiredMiddleware(object):
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.

        response = self.get_response(request)
        if not request.user.is_authenticated: # in Django > 3 this is a boolean
            return HttpResponseRedirect('login')
        
        # Code to be executed for each request/response after
        # the view is called.

        return response
Tobit
  • 406
  • 7
  • 19
IVI
  • 1,893
  • 1
  • 16
  • 19
  • 5
    I think you need to move your `self.get_response(request)` call so that it comes _after_ the `request.user.is_authenticated()` check . The way it is written, an unauthenticated request will run the view code, which will generate a response and then the auth check will replace that response with a redirect to the login page. Any updates/deletes in the view code should be bounded by permission checks anyway, but it's probably better not to take the risk. – Grant McLean May 30 '18 at 00:20
14

You can avoid specifying login_url by setting LOGIN_URL.

Therefore, in settings.py add:

LOGIN_URL = '<some_url>'

And in your views.py annotate relevant functions with only @login_required:

@login_required
def some_view_function(request):

If you need to redirect within a view function, you can do so with:

return redirect_to_login(request.get_full_path())
Rok Strniša
  • 6,781
  • 6
  • 41
  • 53
11

This can be done with middleware.

I've found a really nifty djangosnippet that does exactly what you are asking for. You can find it here, and it looks like:

from django.http import HttpResponseRedirect
from django.conf import settings
from re import compile

EXEMPT_URLS = [compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
    EXEMPT_URLS += [compile(expr) for expr in settings.LOGIN_EXEMPT_URLS]

class LoginRequiredMiddleware:
    """
    Middleware that requires a user to be authenticated to view any page other
    than LOGIN_URL. Exemptions to this requirement can optionally be specified
    in settings via a list of regular expressions in LOGIN_EXEMPT_URLS (which
    you can copy from your urls.py).

    Requires authentication middleware and template context processors to be
    loaded. You'll get an error if they aren't.
    """
    def process_request(self, request):

        assert hasattr(request, 'user'), "The Login Required middleware\
 requires authentication middleware to be installed. Edit your\
 MIDDLEWARE_CLASSES setting to insert\
 'django.contrib.auth.middlware.AuthenticationMiddleware'. If that doesn't\
 work, ensure your TEMPLATE_CONTEXT_PROCESSORS setting includes\
 'django.core.context_processors.auth'."

        if not request.user.is_authenticated():
            path = request.path_info.lstrip('/')
            if not any(m.match(path) for m in EXEMPT_URLS):
                return HttpResponseRedirect(settings.LOGIN_URL)

All you have to do is to save the file as middleware.py and include the class in you're settings.py, i.e.

MIDDLEWARE_CLASSES += ('projectname.common.middleware.RequireLoginMiddleware',)

You can also define a LOGIN_URL in settings.py, so that you'll be redirected to your custom login page. The default LOGIN_URL is '/accounts/login/'.

Martin Hallén
  • 1,492
  • 1
  • 12
  • 27
  • For those like me that were tripped up with recent changes, `is_authenticated()` is no longer a callable, now it's just a boolean var, so remove the `()` :) – laminatefish Dec 12 '19 at 15:42
5

Maybe too late but in django 1.9+ it's too easy. Django introduced Login Required mixin for generic classes and this a great example here by William S. Vincent

simply in your view add LoginRequiredMixin as parent class

from django.contrib.auth.mixins import LoginRequiredMixin

class BlogUpdateView(LoginRequiredMixin, UpdateView):
model = Post
template_name = 'post_edit.html'
fields = ['title', 'body']

Also you can use login_required decorator for method request

Alireza Saremi
  • 401
  • 1
  • 7
  • 13
1
from django.contrib.auth.decorators import login_required

@login_required(login_url='/login/')

def home(request):
    return render(request, "home.html")

It's showing like this: http://127.0.0.1:1235/login/?next=/home/

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
1

I'm using class based views and I couldn't get any of these solutions to work for what I needed so I'll post my working solution here.

class ManagementPageView(TemplateView):

    # Make a dispatch method to handle authentication
    def dispatch(self, *args, **kwargs):

        # Check if user is authenticated 
        if not self.request.user.is_authenticated:

            # Redirect them to the home page if not 
            return redirect('home')
            
        # Render the template if they are 
        return render(self.request, 'management.html')
Casivio
  • 333
  • 7
  • 15