How to require login for Django Generic Views?
Asked Answered
A

11

92

I want to restrict access to URLs handled by Django Generic Views.

For my Views I know that login_required decorator does the job. Also Create/Delete/Update Generic Views take the login_required argument, but I couldn't find a way to do this for other Generic Views.

Allomorph answered 26/1, 2010 at 15:59 Comment(0)
D
107

For Django < 1.5, you can add a decorator by wrapping the function in your urls, which allows you to wrap the generic views:

from django.contrib.auth.decorators import login_required
from django.views.generic.simple import direct_to_template
urlpatterns = patterns('',
    (r'^foo/$', login_required(direct_to_template), {'template': 'foo_index.html'}),
    )

The function-based generic views are deprecated in Django 1.4 and were removed in Django 1.5. But the same principle applies, just wrap the view function of the class based view with the login_required decorator:

login_required(TemplateView.as_view(template_name='foo_index.html'))
Disenthral answered 26/1, 2010 at 16:12 Comment(1)
Here how to specify login_url login_required(TemplateView.as_view(template_name='foo_index.html'))Beaverboard
I
111

Django >= 1.9 or using django-braces

Django 1.9 has introduced a LoginRequiredMixin that is used thus:

from django.contrib.auth.mixins import LoginRequiredMixin

class MyView(LoginRequiredMixin, View):
    login_url = '/login/'
    redirect_field_name = 'redirect_to'

If you are using an older version of django you can use pretty much the same mixin from django-braces - the Django version was based on the django-braces version. django-braces 1.4.x still supports Django 1.4 so you can use it with pretty old versions.

Older Methods

I found this question while googling for how to decorate class based views, so to add the answer for that:

This is covered in the documentation section on decorating class based views. There is the urls.py wrapper, or you can apply the decorator to the dispatch() method. Examples from the documentation:

Decorating in URL conf

from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView

from .views import VoteView

urlpatterns = patterns('',
    (r'^about/', login_required(TemplateView.as_view(template_name="secret.html"))),
    (r'^vote/', permission_required('polls.can_vote')(VoteView.as_view())),
)

Decorating the class

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView

class ProtectedView(TemplateView):
    template_name = 'secret.html'

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super(ProtectedView, self).dispatch(*args, **kwargs)

See the documentation linked to above for more details.

Inventory answered 5/2, 2013 at 14:27 Comment(2)
AWESOME! but I made simple class with only def dispatch method as subclass of View. Now I can simply make something like this: class ProtectedTemplateView(TemplateView, ProtectedView): passIndependent
if I wont set the login_url but set this on settings.py will it redirect to this by default?Tia
D
107

For Django < 1.5, you can add a decorator by wrapping the function in your urls, which allows you to wrap the generic views:

from django.contrib.auth.decorators import login_required
from django.views.generic.simple import direct_to_template
urlpatterns = patterns('',
    (r'^foo/$', login_required(direct_to_template), {'template': 'foo_index.html'}),
    )

The function-based generic views are deprecated in Django 1.4 and were removed in Django 1.5. But the same principle applies, just wrap the view function of the class based view with the login_required decorator:

login_required(TemplateView.as_view(template_name='foo_index.html'))
Disenthral answered 26/1, 2010 at 16:12 Comment(1)
Here how to specify login_url login_required(TemplateView.as_view(template_name='foo_index.html'))Beaverboard
Y
39

The generic views have changed from functions to objects with version 1.3 of Django. As such, there is a slight change needed for Will McCutchen and Will Hardy answers to work with version 1.3:

from django.contrib.auth.decorators import login_required
from django.views.generic import TemplateView

urlpatterns = patterns('',
    (r'^foo/$', login_required(TemplateView.as_view(template_name='foo_index.html'))),
)

Also the documentation describes how to do this as well.

Yellowweed answered 5/5, 2011 at 0:17 Comment(1)
Please, reader, take into account this answer cause time passes by and software evolves. The first solution wasn't working for me.Zielsdorf
P
12

If you don't want to write your own thin wrapper around the generic views in question (as Aamir suggested), you can also do something like this in your urls.py file:

from django.conf.urls.defaults import *

# Directly import whatever generic views you're using and the login_required
# decorator
from django.views.generic.simple import direct_to_template
from django.contrib.auth.decorators import login_required

# In your urlpatterns, wrap the generic view with the decorator
urlpatterns = patterns('',
    (r'', login_required(direct_to_template), {'template': 'index.html'}),
    # etc
)
Pet answered 26/1, 2010 at 16:13 Comment(0)
C
8

For django 1.11, You can use LoginRequiredMixin for Class-based Views

in settings file you should add

LOGIN_URL="/login/"

in your views.py

from django.contrib.auth.mixins import LoginRequiredMixin

class RestaurantLocationCreateView(LoginRequiredMixin,CreateView):
    ....
Controller answered 2/8, 2017 at 5:55 Comment(0)
J
8

Another way to achieve this is below, I like that it is quite similar to how it's done with function-based views and does not require modifying urls.py or overriding dispatch:

@method_decorator(login_required, name='dispatch')
class YourGenericViewSubclass(TemplateView):
    #
    # View methods
    #
Jessikajessup answered 13/2, 2018 at 11:35 Comment(0)
H
6

In Django =>3.0 it gets pretty easy:

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView

@method_decorator(login_required(login_url='/login/'), name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

for reference: https://docs.djangoproject.com/en/3.0/topics/class-based-views/intro/#decorating-the-class

Holbrooke answered 23/6, 2020 at 14:40 Comment(0)
G
3

I wanted a re-usable way to require auth on many views derived from generic views. I created a replacement dispatch function which I can add to my view class in the same way as it's other declarations.

class Index(generic.ListView):
    model = models.HomePage
    dispatch = auth.dispatch

auth.dispatch is where we do the work:

def dispatch(self, request, *args, **kw):
    """Mix-in for generic views"""
    if userSession(request):
        return  super(self.__class__, self).dispatch(request, *args, **kw)

    # auth failed, return login screen
    response = user(request)
    response.set_cookie('afterauth', value=request.path_info)
    return response
Gramicidin answered 24/5, 2012 at 0:1 Comment(0)
Y
1

Use the following:

from django.contrib.auth.decorators import login_required

@login_required
def your_view():
    # your code here
Yvor answered 26/1, 2010 at 16:4 Comment(1)
Based on the date of the question, I assume the OP is asking for a solution for django's class-based generic views... not function-based views.Tovatovar
A
1

The following could solve this issue.

// in views.py:
class LoginAuthenAJAX(View):
    def dispatch(self, request, *args, **kwargs):
        if request.user.is_authenticated:
            jsonr = json.dumps({'authenticated': True})
        else:
            jsonr = json.dumps({'authenticated': False})
        return HttpResponse(jsonr, content_type='application/json')

// in urls.py
    path('login_auth', views.LoginAuthenAJAX.as_view(), name="user_verify"),

//in xxx.html
<script src = “{% static “xxx/script.js” %}” 
var login_auth_link = “{%  url ‘user_verify’ %}”
</script>

// in script.js
        $.get(login_auth_link, {
            'csrfmiddlewaretoken' : csrf_token,
            },
            function(ret){
                if (ret.authenticated == false) {
                    window.location.pathname="/accounts/login/"
                }
                $("#message").html(ret.result);
            }
        )
Anjanette answered 21/8, 2018 at 3:4 Comment(0)
F
0

I had been struggling with finding the answer to this for a long time till I found this workaround.

In models.py do: from django.db import models

class YourLoginModel:
      fullname = models.CharField(max_length=255, default='your_name', unique=True)
      email  = models.EmailField(max_length=255, unique=True)
      username = models.CharField(max_length=255, unique=True)
      password = models.CharField(max_length=255) #using werkzeug's 
                                                  #generate_password_hash on plaintext password before committing to database model

In forms.py do:

from django import forms
from .models import YourLoginModel

class LoginForm(forms.ModelForm):
      class Meta:
            model = YourLoginModel
            fields = ('username', 'password')

In views.py login logic:

def login(request):
    #login logic here
     # init empty form
    form = LoginForm()

    if request.method == 'POST':

        try:
            # peforms a Select query in db and gets user with log in username
            user_logging_in = User.objects.get(username=request.POST['username'])

            # assign user hash to var
            hash = user_logging_in.password

            # assign form str passs word to var
            password = request.POST['password']

        # if the user does not exist
        except ObjectDoesNotExist:
            html_response = 'User does not exists'
            return HttpResponse(html_response)

        # peform password and hash check
        if check_password_hash(hash, password):
 
            #using sessions cookies to know who we're interacting with
            request.session['username'] = request.POST['username']

            #set expiry date of the session
            request.session.set_expiry(0) # 0 means when the browser is closed

            return redirect('yourapp:home')
        else:
            return HttpResponse('password was incorrect')

    html = 'Login'
    return render(request, 'login.html', {'form': form})

In app view you want to perform login_required on do

from django.views.generic import TemplateView

class yourTemplateView(TemplateView):
      template_name = 'your_template.html'
      def dispatch(self, request, *args, **kwrags):
           if not request.session.has_key('username'):
              #return HttpResponse('not logged in')
              return redirect('yourapp:login.html')
           else:
              return render(request, 'your_view.html')
Filet answered 29/12, 2020 at 21:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.