1

I tried to make a email login/authenticate in views.py, but it returns 'user is None'. I tried to use just email for login not username. If I tried to login with email, it seems to take 'user is None' with custom error messages 'invalid credentials' in views.py.

Django version: 3.0.4 // Model: Custom User Model (AbstractBaseUser) -> USERNAME_FIELD = 'email' // Backend: ModelBackend -> use email for username

Problem 1: def signin in Views.py returns 'user is None' // Problem 2: model have a password(default), password1 and password2(both defined by UserCreationForm)

  1. users_models.py
from django.conf import settings
from django.contrib.auth import authenticate
from django.contrib.auth.models import AbstractBaseUser
from django.db import models
from django import forms
from django.utils import timezone

from .users_managers import UserManager

class User(AbstractBaseUser):
    USERNAME_FIELD = 'email'

    email = models.EmailField(
        verbose_name='email',
        max_length=255,
        db_index=True,
        unique=True,
    )
    password1 = PasswordModelField('password', max_length=50, error_messages={something},)
    password2 = PasswordModelField('password check', max_length=50, error_messages={something},)
    ...
    objects = UserManager()

    class Meta:
        db_table = 'users'
        verbose_name = 'user'
        verbose_name_plural = 'users'

  1. users_forms.py
from django import forms
from django.contrib.auth import authenticate, get_user_model
from django.contrib.auth.forms import ReadOnlyPasswordHashField

from .users_models import User

class UserCreationForm(forms.ModelForm):
    password1 = forms.CharField(
        label='password',
        strip=False,
        widget=forms.PasswordInput(attrs={'placeholder': 'something'}),
        error_messages={something},
    )
    password2 = forms.CharField(
        label='password check',
        widget=forms.PasswordInput,
        error_messages={something},
    )

   class Meta:
        model = User
        fields = ('email', 'password1', 'password2', ... )
   ...
   def save(self, commit=True):
        user = super().save(commit=False)
        user.set_password(self.cleaned_data['password1']) # set_password

        if commit:
            user.save()
        return user

class UserLoginForm(forms.Form):
    email = forms.EmailField(
        label='email',
        max_length=255,
        widget=forms.TextInput(attrs={'autofocus': True}),
    )
    password = forms.CharField(
        label='password',
        strip=False,
        widget=forms.PasswordInput,
    )
  1. backend.py
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
# from django.contrib.auth.models import check_password
from django.contrib.auth.hashers import check_password

class EmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None):
        user_model = get_user_model()
        if '@' in username:
            kwargs = {'email': username}
        else:
            kwargs = {'username': username}
        try:
            user = user_model.objects.get(**kwargs)
            if user.check_password(password):
                return user
            else:
                return None
        except user_model.DoesNotExist:
            return None

    def get_user(self, id=None):
        user_model = get_user_model()
        try:
            return user_model.objects.get(pk=id)
        except user_model.DoesNotExist:
            return None
  1. settings.py
# AUTH_USER_MODEL = 'apps.User' # if I use this code, It returns errors. 

AUTHENTICATION_BACKENDS = [
    'apps.backend.EmailBackend',
    'django.contrib.auth.backends.ModelBackend',
]
  1. login.html
{% extends 'apps/base.html' %}
{% load static %}
{% block content %}
<div class="container">
    <h1>login</h1>
    {% if error %}
        <p>{{ error }}</p>
    {% endif %}
    <form method="POST" action="{% url 'login' %}">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" class="btn btn-primary" value="login">
    </form>
</div>
{% endblock %}
  1. views.py
from django import forms
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.template import loader
from django.contrib import auth, admin
from django.contrib.auth import login, authenticate
from django.contrib.auth.models import User, Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.redirects.models import Redirect
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponseRedirect
from django.apps import AppConfig

from apps.users_forms import UserCreationForm, UserLoginForm, UserChangeForm
from apps.users_models import User

@csrf_exempt
def signup(request):
    if request.method == 'POST':
        creation_form = UserCreationForm(request.POST)
        if creation_form.is_valid():
            creation_form.save()
            return redirect('index')
    else:
        creation_form = UserCreationForm()
    return render(request, 'signup.html', {'form': creation_form})


@csrf_exempt
def signin(request):
    if request.user.is_authenticated:
        return redirect(reverse('index'))
    if request.method == "POST":
        login_form = UserLoginForm(request.POST)

        if login_form.is_valid():
            username = login_form.cleaned_data['email'] # take email in form for username
            password = login_form.cleaned_data['password']
            user = authenticate(username=username, password=password)
            if user is not None: # not going here !!!
                if user.is_active:
                    login(request, user)
                    return redirect('index')
            else:
                return render(request, 'login.html', 
                    {'form': login_form, 'error': 'invalid credentials'}) 
                # errors here !!!!! always views.py returns this messages !!!!!
    else:
        login_form = UserLoginForm()
        return render(request, 'login.html', {'form': login_form})

How can I solve this problems?

1
  • cand you put a print(user_model) in your backend.py? Commented Apr 3, 2020 at 7:16

2 Answers 2

0

try this

user = authenticate(email=email, password=password)

or use check_password

from django.contrib.auth.hashers import check_password


# inside view function
# ...
user = User.objects.get(email=email)
if check_password(password, user.password):
    if user.is_active:
        login(request, user)
# ...
Sign up to request clarification or add additional context in comments.

2 Comments

first one returns 'User is None' nevertheless I use email = login_form.cleaned_data['email'] for user = authenticate(email=email, password=password). In second solution, It still seems to have a error. I changed login(request, user) to login(request, user, backend='apps.backend.EmailBackend') or django.contrib.auth.backends.ModelBackend'). And, views.py returns redirect 'index' but it seems to be not logined. Because I display 'user.email' in index.html with {% if user.is_authenticated %} <p>hello, {{ user.email }}</p>{% endif %} but index don't show the user.email..
about second solution: After login(request, user, backend=..), return redirect('index') and browser redirect to index.html but there are no user.is_authenticated. If user is authenticated, the {{ user.email }} will appear in index.html but it doesn't.
0

I puzzled with this problem for four days. The problem was that me, as you I was using the user.save() and I could not see what the problem was, but then I looked at it with debug eyes, and it turns out that if you use user.save() it messes with the hashing of the password somehow, don't ask me how I don't know. So I worked around it using the user.create() method that Django provides, worked like a charm:

@api_view(['POST'])
def create_user(request):
    new_user = UserSerializer(data=request.data)
    if new_user.is_valid():
        user_saved = new_user.create(request.data)
        return Response('User {} created'.format(user_saved.username), 
                                           status=status.HTTP_200_OK)
    else:
        return Response('User not created', status=status.HTTP_200_OK)

I used something like this, but you can do as you wish just use the user.create().

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.