【问题标题】:clean function causing .is_valid() method to return false when used in form django 1.9.8clean 函数导致 .is_valid() 方法在表单 django 1.9.8 中使用时返回 false
【发布时间】:2016-07-28 01:10:32
【问题描述】:

在我正在处理的一个 django 项目中,我编写了一个函数来验证来自登录表单的数据。现在,每当我在登录表单的实例上运行 is_valid() 方法时,它每次都会返回 false。我整天都在试图找出导致这个问题的原因。

forms.py:

 from django import forms
 from django.contrib.auth import authenticate, password_validation
 from django.contrib.auth.forms import AuthenticationForm
 from django.contrib.auth.models import User, Group
 from .functions import *
 from .models import *

 class login_form(forms.Form):
     username = forms.CharField(max_length=64, label='Username', widget=forms.TextInput(attrs={'placeholder': 'username...', 'class':'login-field'}))
     password = forms.CharField(max_length=24, label='Password', widget=forms.PasswordInput(attrs={'placeholder': 'password...', 'class':'login-field'}))

     def clean(self, *args, **kwargs):
         username = self.cleaned_data.get('username', None)
         password = self.cleaned_data.get('password', None)
         user_obj = None
         self._errors['username'] = None  #clears default username error
         self._errors['password'] = None  #clears default password error

         if username is not None:
             try:
                 user_obj = User.objects.get(username=username) 

                 if password_validation.validate_password(password, user_obj) is None:
                     self._errors['password'] = 'Password is incorrect. Please try again.'

             except User.DoesNotExist:
                 self._errors['username'] = 'User does not exist. Please use valid credentials.'

         else:
             self._errors['username'] = 'Please type a valid username.'


         return super(login_form, self).clean(*args, **kwargs)

views.py:

from django.shortcuts import render, redirect
from django.http import HttpRequest, JsonResponse, HttpResponse
from django.template import RequestContext
from django.conf.urls import patterns
from django.contrib import admin
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User, Group
from datetime import datetime
from .models import *
from .forms import *
from .functions import *
from os import *
import shutil
import json


 def home(request):
     if request.method == 'POST':
        form = login_form(request.POST)

        if form.is_valid():
            username = form.data.get('username')
            password = form.data.get('password')
            authenticate_usr_obj = authenticate(username=username, password=password)
            if authenticate_usr_obj is not None:
                login(request, authenticate_usr_obj)
                return redirect(dashboard)

     else:
         form = login_form()


     try:
        logout_successful = request.session.get('logout_complete', False)
        request.session['logout_complete'] = False

     except:
         logout_successsful = False

     context = {
        'body_class': 'login',
        'login': form,
        'logout_successful': logout_successful
     }

     return render(request, 'development/index.html', context)

【问题讨论】:

    标签: django validation django-forms python-3.5 django-login


    【解决方案1】:

    通过阅读 Forms API 文档,我可以看到,在您的示例中,您在尝试将数据排成一行之前没有清理数据:

    username = self.cleaned_data.get('username', None)
    password = self.cleaned_data.get('password', None)
    

    尝试首先使用 clean() 来验证数据,如下所示:

    cleaned_data = super(login_form, self).clean()
    

    那么你可以尝试这样获取它们:

    username = cleaned_data.get('username', None)
    password = cleaned_data.get('password', None)
    

    你可以找到更多here

    版本 2:

    好的,首先我在 Django 文档中找到了关于 django.contrib.auth.password_validation.validate_password() 的内容:

    Integrating validation

    validate_password(password, user=None, password_validators=None)

    验证密码。 如果所有验证器都认为密码有效,则返回无。如果一个或多个验证器拒绝密码,则引发 ValidationError 并包含来自验证器的所有错误消息。

    用户对象是可选的:如果没有提供,一些验证器可能无法执行任何验证并会接受任何密码。

    所以行:

    if password_validation.validate_password(password, user_obj) is None:

    应该是:

    if password_validation.validate_password(password, user_obj) is not None:

    其次:

    Form.errors

    >>>> f.errors
    {'sender': ['Enter a valid email address.'], 'subject': ['This field is required.']}`
    

    在此字典中,键是字段名称,值是表示错误消息的 Unicode 字符串列表。错误消息存储在列表中,因为一个字段可以有多个错误消息。

    因此,基于此,我将更改您定义自定义消息的行,使其看起来更像这样:

    self.errors['password'] = [u'Password is incorrect. Please try again.']
    

    最后要记住的一件事是 clean 方法必须返回 clean_data。

    forms.py 代码的最终版本可能如下所示:

    def clean(self):
        super(login_form,self).clean()
        username = self.cleaned_data.get('username', None)
        password = self.cleaned_data.get('password', None)
    
        if username is not None:
            try:
                user_obj = User.objects.get(username=username) 
                if password_validation.validate_password(password, user_obj) is not None:
                    self.errors['password'] = [u'Password is incorrect. Please try again.']
            except User.DoesNotExist:
                self.errors['username'] = [u'User does not exist. Please use valid credentials.']
        else:
            self.errors['username'] = [u'Please type a valid username.']
        return self.cleaned_data
    

    希望这次能有所帮助。

    【讨论】:

    • 我一开始尝试这样做,它仍然返回 false
    • 您实际上想通过编写自己的clean() 方法来实现什么?它只是关于表单中的自定义错误消息吗?因为您在clean() 方法中尝试做的所有事情(当然除了错误消息)都可以使用默认方法完成(不覆盖它) - 在我看来。
    • 我不仅想覆盖默认错误消息,而且我发现默认错误不够动态。我想在验证期间包含一些逻辑,以便我可以提供更具体的错误消息(即,如果另一个用户共享相同的电子邮件,则用户不存在,在其他人之前填写特定字段。)。我试图创建一种比默认方法更动态的验证方法。
    • @TechEmperor95 您可能想查看更新后的答案并试一试。
    • 我唯一的问题是如何清除其他错误。我清除了用户名和密码的错误值,因此一次只会以特定顺序出现。
    【解决方案2】:

    在上面测试了你的代码,我遇到了由于表单 self._errors 而导致的错误

    `Nonetype` object is not iterable
    

    这是我的建议

    forms.py

    from django import forms
    from django.contrib.auth.models import User
    from django.core.exceptions import ValidationError
    
    
    class login_form(forms.Form):
        username = forms.CharField(
            max_length=64, label='Username',
            widget=forms.TextInput(
                attrs={'placeholder': 'username...', 'class': 'login-field'}))
        password = forms.CharField(
            max_length=24, label='Password',
            widget=forms.PasswordInput(
                attrs={'placeholder': 'password...', 'class': 'login-field'}))
    
    def clean_username(self):
        data = self.cleaned_data.get('username', None)
        if not data:
            raise ValidationError('Please type a valid username.',
                                  code='invalid')
        return data
    
    def clean(self, *args, **kwargs):
        username = self.cleaned_data.get('username')
        password = self.cleaned_data.get('password')
        try:
            user_obj = User.objects.get(username=username)
            if not user_obj.check_password(password):
                raise ValidationError(
                        'Password is incorrect. Please try again',
                        code='incorrect')
    
        except User.DoesNotExist:
            raise ValidationError(
                'User does not exist. Please use valid credentials.',
                code='invalid')
        return super(login_form, self).clean(*args, **kwargs)
    

    views.py

    from django.shortcuts import render
    from django.contrib.auth import authenticate, login
    from django.contrib.auth import logout
    
    from .forms import *
    
    
    def home(request):
        logout_successful = True
        if request.method == 'POST':
            form = login_form(request.POST)
            # print form.errors
    
            if form.is_valid():
                username = form.data.get('username')
                password = form.data.get('password')
                authenticate_usr_obj = authenticate(
                    username=username, password=password)
                if authenticate_usr_obj:
                    login(request, authenticate_usr_obj)
                    # print 'ok'
        else:
            form = login_form()
        logout_successful = request.user.is_authenticated()
        return render(request, 'base.html', {
            'body_class': 'login',
            'login': form,
            'logout_successful': logout_successful
        })
    
    
    def logout_view(request):
        logout(request)
    

    我还是一个初学者,所以请随意评论一些东西

    【讨论】:

    • 我试过了,它将错误存储为非字段错误。这是一个不错的尝试,但不完全是我想要的。
    • 如果你想设置 Password is incorrect. 和 Please type a valid username 你可以在 self.errors[''] = [u'string here'] 上使用你的代码来设置错误作为字段错误
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-22
    • 2016-09-12
    • 2011-07-02
    • 2020-07-08
    • 2015-04-17
    • 1970-01-01
    相关资源
    最近更新 更多