driftking9987
driftking9987

Reputation: 1713

Use login_required decorator in Django 2.1

I have a basic requirement, I have a login page, where after a user logs in, it redirects to a page.

If the user is not logged in, and he opens that url, he gets redirected to the login page and after successful login, he is redirected to the url he opened.

Login : http://127.0.0.1:8000/w_dashboard/login/

Another page : http://127.0.0.1:8000/w_dashboard/roaster/

I have an app named w_dashboard .

Now in setting page, I have declared

Settings.py

STATIC_URL = '/static/'
LOGIN_URL = '/w_dashboard/login/'

Login.html

<div class="login">
            <form action='{% url "login" %}' method="POST">
                {% csrf_token %}
                <input type="text" placeholder="username" name="user" id="user"><br>
                <input type="password" placeholder="password" name="password" id="pwd"><br>
                <input type="submit" value="Login">
            </form>
                <!-- Categories: success (green), info (blue), warning (yellow), danger (red) -->
                {% for message in messages %}
                        <div class="alert alert-{{ category }} alert-dismissible" role="alert">
                            <button type="button" class="close" data-dismiss="alert" aria-label="Close"><span
                                    aria-hidden="true">&times;</span></button>
                            <!-- <strong>Title</strong> --> {{ message }}
                        </div>
                {% endfor %}
        </div>

w_dashboard's URL file :

from django.urls import path
from . import views
from django.conf.urls import url

urlpatterns = [
    #url(r'^$', views.HomePageView.as_view()),
    url(r'^$', views.LoginPageView.as_view()),
    url(r'^login/$', views.LoginPageView.as_view(), name = 'login'),
    url(r'^roaster/$', views.RoasterPageView.as_view(), name="roaster"),
    url(r'^manual_login/$', views.RoasterPageView.as_view(), name="mLogin"),

]

Now, w_dashboard views.py :

class LoginPageView(TemplateView):
    def get(self, request, *args, **kwargs):
        if request.method == 'GET':
            print("Get request")

            return render(request, 'login.html', context=None)

    def post(self, request, *args, **kwargs):
        if request.method == 'POST':
            print("POST request")
            print(request.POST['user'])
            a = models.login_validate(request.POST['user'],request.POST['password'])
            if a[0] == 0:
                print("inside if ")
                messages.error(request, 'username or password not correct')
                return render(request, 'login.html', context=None)
            elif a[0] == 1:
                print("inside elif")
                user = emp_user()
                user.name = a[1]
                print(user.last_login)
                print(user.name)
                user.save()
                login(request, user)
                return redirect(request.GET.get('next', 'roaster'))
            else:
                print("in else")
                return render(request, 'login.html', context=None)


class RoasterPageView(TemplateView):
    @login_required
    def get(self, request, *args, **kwargs):
        print(request, request.user)
        return render(request, 'roaster.html', context=None)

I have a custom model name emp_user in w_dashboard models.py.

class emp_user(models.Model):
    name = models.TextField()
    last_login = models.DateTimeField(default=datetime.now)

Now the issue is that am getting following error :

Internal Server Error: /w_dashboard/roaster/
Traceback (most recent call last):
  File "/Users/driftking9987/.conda/envs/w_management/lib/python3.6/site-packages/django/core/handlers/exception.py", line 34, in inner
    response = get_response(request)
  File "/Users/driftking9987/.conda/envs/w_management/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "/Users/driftking9987/.conda/envs/w_management/lib/python3.6/site-packages/django/core/handlers/base.py", line 124, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/Users/driftking9987/.conda/envs/w_management/lib/python3.6/site-packages/django/views/generic/base.py", line 68, in view
    return self.dispatch(request, *args, **kwargs)
  File "/Users/driftking9987/.conda/envs/w_management/lib/python3.6/site-packages/django/views/generic/base.py", line 88, in dispatch
    return handler(request, *args, **kwargs)
  File "/Users/driftking9987/.conda/envs/w_management/lib/python3.6/site-packages/django/contrib/auth/decorators.py", line 20, in _wrapped_view
    if test_func(request.user):
AttributeError: 'RoasterPageView' object has no attribute 'user'
[31/Jan/2019 09:42:42] "GET /w_dashboard/roaster/ HTTP/1.1" 500 72039

Any leads will be helpful. I just want the login page to redirect to roaster page. And if am opening url of roaster, then it should redirect to login and then again to roaster.

I also need the logged in user's name in roaster so that I can display it( am saving the user, but it's not giving the desired output)

And also, if the user logs in, for how long it will be logged in? I mean, let's say I log in and somehow correctly get redirected to roaster page, then if I manually open the roaster page, I shouldn't get redirected to login and for how long this situation can last?

Thanks

Upvotes: 0

Views: 2669

Answers (3)

Mr Coder
Mr Coder

Reputation: 523

django has inbuilt auth and permission access.

django decorator


from django.contrib.auth.decorators import login_required
# Create your views here.

@login_required(login_url='login')
def dashboard (request):
    
     return render(request, 'dash/index.html')

```

this view only access by the logged in user 

Upvotes: 0

driftking9987
driftking9987

Reputation: 1713

This answer is basically for someone who is just starting out, might not be the most optimised code but at least it get's you up and running.

First of all, let's create a custom model in models.py .

class AuthUser(AbstractBaseUser):
    password = models.CharField(max_length=128)
    last_login = models.DateTimeField(blank=True, null=True)
    is_superuser = models.IntegerField()
    username = models.CharField(unique=True, max_length=150)
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=150)
    email = models.CharField(null=False, unique=True, max_length=254)
    is_staff = models.IntegerField()
    is_active = models.IntegerField()
    date_joined = models.DateTimeField()

    USERNAME_FIELD = 'email'

    class Meta:
        managed = False
        db_table = 'auth_user'

Declare it in setting.py.

STATIC_URL = '/static/'
LOGIN_URL = '/w_dashboard/login/'
AUTH_USER_MODEL = 'w_dashboard.AuthUser'


AUTHENTICATION_BACKENDS = [
    'w_dashboard.auth_backends.MySQLAuthBackend',
]

In my case, I had to query the username/password from a mysql table. So I have to make changes accordingly.

First, in settings.py add your database details.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'w_s',
        'USER': 'r09ot',
        'PASSWORD': 'qw5',
        'HOST': '119.00.00.000',
        'PORT': '3306',
    }
}

Now this will create few tables in database, and we will be using the auth_user table created by Django to store the username and password.

Now, let's create the custom backend. Create a file named auth_backends.py.

Add the following code :

from django.contrib.auth import get_user_model, backends
from .models import AuthUser


class MySQLAuthBackend:

    def authenticate(self, request, email=None, password=None):
        print("inside auth func")
        usermodel = get_user_model()
        try:
            user = usermodel.objects.get(email=email)
            if not user:
                print("No such user")

            else:
                print("abc")
                if user.password == password:
                    print("password match")
                    return user
            # if user.check_password(password):
            #     return user

        except usermodel.DoesNotExist:
            print("inside except")
            return None

    def get_user(self, pk):
        usermodel = get_user_model()
        try:
            usermodel = get_user_model()
            return usermodel.objects.get(pk=pk)
        except usermodel.DoesNotExist:
            pass

        return None

The above code will query the database with username as "email" and password as "password". And return the user.

Now let's come to Login function.

def post(self, request, *args, **kwargs):
    if request.method == 'POST':
        print("POST request")
        print(request.POST['user'])
        user = authenticate(request, email=request.POST['user'], password=request.POST['password'])
        if user is not None:
            print("login success")
            user.save()
            print(user.__dict__)
            login(request, user)
            request.session.set_expiry(20)
            return redirect(request.GET.get('next', 'mLogin'))
        else:
            print("login unsuccess")
            messages.error(request, 'username or password not correct')
            return render(request, 'login.html', context=None)

Now this is the most important part. Since we authenticated the user from custom backend authentication method, user is being populated in a way which is acceptable to login method to accept. In a nutshell, it let's you login into the login function.

Now comes the part of login_required. Since it's a view, we will use method_decorator like this :

@method_decorator(login_required, name='dispatch')
class ManualLoginPageView(TemplateView):
    def get(self, request, *args, **kwargs):
        print("inside manual login")
        print(request.user.__dict__)
        print(request.user.get_short_name())
        return render(request, 'manualLogin.html', context=None)

Also in url.py, Add login_required like

url(r'^roaster/$', login_required(views.RoasterPageView.as_view()), name="roaster"),

This is working and executing the functionalities as intended.

Upvotes: 0

Alex Baranowski
Alex Baranowski

Reputation: 1084

The auth decorators should be used for functions (not methods in the class). For class-based views, you should use mixins like LoginRequiredMixin. More information with samples can be found in official Django documentation - https://docs.djangoproject.com/en/2.1/topics/auth/default/

EDIT: You are also using custom model for a user - how to implement custom user or extend user model used by Django auth can be found here: https://docs.djangoproject.com/en/2.1/topics/auth/customizing/

Upvotes: 0

Related Questions