Form组件数据库校验

Form组件的功能有校验输入内容和生成HTML代码,校验的一些基础功能,诸如:

  1. 自定义一个继承form.Form的类;
  2. 实例化类,传入前端
  3. 获得返回数据进行判断obj.is_valid()
  4. 自定制输入的错误类型,返回给前端

这些基础校验只是在我们进行简单地input的时候进行使用的,然而真正的校验,应该是和数据库联系起来,如何才能校验用户输入的数据与数据库里的数据是否重复,诸如此类的检验呢?

django自然给我们提供了这些简单的接口,在基础功能中,我们校验成功(obj.is_valid()判断成功)就会给我们返回obj.cleaned_data,这个cleaned_data中便是我们可以下功夫的东西,顺着cleaned_data所在的文件寻找我们发现,django通过执行full_clean方法里面的各种函数,来给我们返回cleaned_data这个字典:

    def full_clean(self):
        """
        Clean all of self.data and populate self._errors and self.cleaned_data.
        """
        self._errors = ErrorDict()
        if not self.is_bound:  # Stop further processing.
            return
        self.cleaned_data = {}
        # If the form is permitted to be empty, and none of the form data has
        # changed from the initial data, short circuit any validation.
        if self.empty_permitted and not self.has_changed():
            return

        self._clean_fields()
        self._clean_form()
        self._post_clean()

首先会执行self._clean_fields()这个方法,我们再来看这个方法的代码:

    def _clean_fields(self):
    #循环我们自己定义的form类中的每一个字段
        for name, field in self.fields.items():
            # value_from_datadict() gets the data from the data dictionaries.
            # Each widget type knows how to retrieve its own data, because some
            # widgets split data over several HTML fields.
          
            if field.disabled:
                value = self.get_initial_for_field(field, name)
            else:
                value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
            try:
                if isinstance(field, FileField):
                    initial = self.get_initial_for_field(field, name)
                    value = field.clean(value, initial)
                else:
                    value = field.clean(value)
                #从HTML中获得数据,然后将值添加到cleaned_data这个字典中
                self.cleaned_data[name] = value
                if hasattr(self, 'clean_%s' % name):
                    value = getattr(self, 'clean_%s' % name)()
                    self.cleaned_data[name] = value
            except ValidationError as e:
                self.add_error(name, e)

我们发现在给字典赋值这个操作是在try中的,如果出错则会返回错误信息,大概猜测,我们在自己定义的类中的错误信息是在这个excep中被赋值的。在try中,赋值完之后,又进行了一步if判断,
if hasattr(self, 'clean_%s' % name),在找了源码下面的方法发现并没有’clean_%s’这个方法,说明,这个if就是留给程序员进行扩展的接口,如果我们自己定义了这个clean_%s’方法,那么我们就可以对数据进一步校验并返回结果,那么刚才赋值的’cleaned_data只能算是一个临时的数据。

那么我们就可以在我们自己的form类中添加上这个’clean_%s’方法(注意,由于整个操作都在for循环中,所以这里的方法,一次只能针对一个字段进行校验,不能校验你的全部字段)

	#定义clean方法,这里的name对应自己的字段名
    def clean_name(self):
    #首先我们要拿到之前临时传入字典里的那个值
        res = self.cleaned_data['name']
        #我们将这个值放入数据库中,进行查找,.count()方法,查看数据条数,如果只有一个返回1
        if Members.objects.filter(name=res).count():
        #如果返回了1说明我们这个用户名已经在数据库中存在了,我们就不能让用户输入,那么我们如何才能反馈错误呢,我们注意到之前有个except ValidationError,那么我们猜测只要我们在这里raise 这个错误,然后自定义错误加入error字典中,即可添加返回给用户额
            raise ValidationError('用户名已经存在')
        else:
        #如果没有就可以返回这个数据
            return res

这样我们再让用户注册时,就不能再注册相同的用户名了。当然不局限于查重这个功能,实现具体的方法,要看自己的需求。

上面这个方法只能针对单独字段进行校验,那么当我们需要校验form中的所有的值的时候,就不能用上面这个方法,当然从方法名字也能看出上面的方法是从_clean_fields中衍生出来的,_clean_fields顾名思义就是校验字段的。

再来看源码:

        self._clean_fields()
        self._clean_form()
        self._post_clean()

在_clean_fields()后面还有2个函数需要执行,查找这2个函数:

    def _clean_form(self):
        try:
            cleaned_data = self.clean()
        except ValidationError as e:
            self.add_error(None, e)
        else:
            if cleaned_data is not None:
                self.cleaned_data = cleaned_data

    def _post_clean(self):
        """
        An internal hook for performing additional cleaning after form cleaning
        is complete. Used for model validation in model forms.
        """
        pass

我们发现_clean_form()是一个半定制方法,而_post_clean()是一个全部需要自己定制的方法,在一般情况下,在能力不够的情况下,不要轻易修改源码,我们使用_clean_form()就足够了,在这个方法中,try了一个self.clean()的方法,将值赋给了cleaned_data,那么,猜想这里就是进行进一步校验的地方,查看这个方法:

    def clean(self):
        """
        Hook for doing any extra form-wide cleaning after Field.clean() has been
        called on every field. Any ValidationError raised by this method will
        not be associated with a particular field; it will have a special-case
        association with the field named '__all__'.
        """
        return self.cleaned_data

除了给了一个返回的cleaned_data其他也需要定制,那么我们就可以将这个方法,加到我们自己的类里去了:

	def clean(self):
        valuedict = self.cleaned_data
        name=valuedict.get('name')
        height=valuedict.get('height')
        if name == 'abc' and height == 180:
            raise ValidationError('出错')
        else:
            return valuedict

这里需要特别注意一个问题,当时在这里,我用另一种写法,如果上面的校验出现了错误,那么整个网页就会报错,我的第一种写法是:

			name = self.cleaned_data['name']
            height = self.cleaned_data['height']
            print(name == 'abc', height == 180)
            if name == 'abc' and height == 180:
                raise ValidationError('这个名字已经被锁定了。')
            else:
                print('ok')
                return self.cleaned_data

看似这2个写法并没有什么问题,实则差距很大,在python中取字典中的值的时候一般会有两种可选的方法——get() 方法和 [key] 方法,一般来说没什么太大区别,但是在取没有在字典中出现的值时,用get方法不会报错,会返回一个none,而[key]方法,会直接报错:KeyError,中止代码。如果你在校验字段时,已经raise出错误,那么cleaned_data这个字典里不会出现这个字段名,如果这时候到了这一步,用[key]取值,由于没有这个字段就会报错,导致程序中止,而用get只会返回一个None,程序会继续运行,一个小细节问题。

以上就是对Form的自定义校验的方法!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值