Search code examples
djangodjango-formsdjango-viewsdjango-authentication

After overriding the Clean method in AuthenticationForm, authenticate returns None, and does not handle any errors


friend.

There was a need to override the Сlean method in AuthenticationForm to further check for the presence of an attribute before the user can log in.

Forms.py:

from django.contrib.auth.forms import AuthenticationForm as BaseAuthenticationForm
from django.contrib.auth.models import User
from inc.models import Account, UserProfile, Counter

class AuthenticationForm(BaseAuthenticationForm):

    def clean(self):
    username = self.cleaned_data.get('username')
    user = User.objects.filter(username = username).first()
    if user != None:
        print(user)
        if not user.is_superuser and not user.is_staff:
            account = Account.objects.filter(num_account = UserProfile.objects.filter(user__username = username).first().num_account).first()
            have_counter = Counter.objects.filter(num_account = account).all()
            if not have_counter:
                raise forms.ValidationError('Some text...')
    return self.cleaned_data

Views.py:

from django.contrib.auth import login, authenticate
from .forms import AuthenticationForm

def LogIn(request):
    if request.method == 'POST':
    form = AuthenticationForm(data=request.POST)
    if form.is_valid():
        username = form.cleaned_data.get('username')
        password = form.cleaned_data.get('password')
        user = authenticate(username=username, password=password)
        print(user)
        login(request, user)
        return redirect('/')
    else:
        print(form.errors)
else:
    form = AuthenticationForm()
return render(request, 'userprocessing/login.html', {'form': form})

The problem is as follows. When I try to register as a user who is in the database everything works as it should, but when I enter a non-existent login I throw out:

AnonymousUser' object has no attribute '_meta'

which points to login(request, user). The error is understandable, it can not login the user which does not return user = authenticate(username=username, password=password) (it returns None).

The question is why it does not handle the error, there must be a message check the username or password.

Perhaps I did not correctly redefine Clean method in AuthenticationForm?

UPD:

Traceback:

Request Method: POST
Request URL: http://localhost:8000/login/?next=/

Django Version: 2.0.5
Python Version: 3.6.5
Installed Applications:
['django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'inc.apps.IncConfig',
 'main.apps.MainConfig',
 'django.contrib.admin']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "...\Python\Python36-32\lib\site-packages\django\core\handlers\exception.py" in inner
  35.             response = get_response(request)

File "...\Python\Python36-32\lib\site-packages\django\core\handlers\base.py" in _get_response
  128.                 response = self.process_exception_by_middleware(e, request)

File "...\Python\Python36-32\lib\site-packages\django\core\handlers\base.py" in _get_response
  126.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "...\views.py" in LogIn
  57.             login(request, user)

File "...\Python\Python36-32\lib\site-packages\django\contrib\auth\__init__.py" in login
  155.     request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)

File "...\Python\Python36-32\lib\site-packages\django\utils\functional.py" in inner
  216.         return func(self._wrapped, *args)

Exception Type: AttributeError at /login/
Exception Value: 'AnonymousUser' object has no attribute '_meta'

Solution

  • In your clean method, you are only raising an error in a case the user exists but is not a superuser neither a staff.

    You should also raise an error when the user does not exist at all.

    I would do it this way using try, except, else.

    def clean(self):
        cleaned_data = super().clean()
        username = cleaned_data.get('username')
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise forms.ValidationError('User does not exist...')
        else:
            print(user)
            if not user.is_superuser and not user.is_staff:
                account = Account.objects.filter(num_account=UserProfile.objects.filter(user__username=username).first().num_account).first()
                have_counter = Counter.objects.filter(num_account=account).all()
                if not have_counter:
                    raise forms.ValidationError('Some text...')
        return cleaned_data
    

    PS. I did not check the logic of your clean method except for whether the user exists or not.

    Check Django's docs about validating forms. Also on why you should call super inside your overridden clean().