60.Django Form表单实现自定义字段

在《Django Form表单内置字段详解》一节中,我们介绍了 Django Form 表单的内置字段,但是在某些业务场景下,内置字段无法满足我们的业务需求,此时就需要我们使用到 Django Form 表单提供的自定义字段的功能。不仅如此,Django Form 表单还给开发者提供了自定义验证规则的功能,可以让开发者对内置字段或者自定义字段添加额外的校验逻辑,从而满足实际的开发任务需要。在本节我们先对如何实现自定义 Form 表单字段做讲解。

1. 实现自定义Field字段

1) Field基类clean方法源码解读

在介绍 Form 表单的内置字段时,我们提及过每一个 Field 字段都一个 clean 方法。Field 字段通过 clean() 校验并获取字段值,所以自定义 Field 字段,简而言之就是自己去实现 clean 方法。自定义字段也要继承 Field 类,那么我看一看,Field 类中的 clean 方法是怎么实现数据的“清洗”作用的,clean 方法的源码如下所示:

  1. def clean(self, value):
  2. """
  3. 验证给定的值并将其“清洗”后的值作为合适的Python对象返回。
  4. 对任何错误抛出ValidationError。
  5. """
  6. value = self.to_python(value)
  7. self.validate(value)
  8. self.run_validators(value)
  9. return value

从 源码分析来看,clean 方法接受一个 value 参数,分别经过三个方法的处理,返回已“清洗”的 value 或者是抛出异常 ValidationError。这个三个方法的源码如下所示:

  1. def to_python(self, value):
  2. return value
  3.  
  4. def validate(self, value):
  5. if value in self.empty_values and self.required:
  6. raise ValidationError(self.error_messages['required'], code='required')
  7.  
  8. def run_validators(self, value):
  9. if value in self.empty_values:
  10. return
  11. errors = []
  12. for v in self.validators:
  13. try:
  14. v(value)
  15. except ValidationError as e:
  16. if hasattr(e, 'code') and e.code in self.error_messages:
  17. e.message = self.error_messages[e.code]
  18. errors.extend(e.error_list)
  19. if errors:
  20. raise ValidationError(errors)

我们分析一下这三个方法的作用。分别如下所示:

  • to_python:实现数据的转换,将传递进来的 value 转换成需要的 Pyhton 对象,例如,对于 TimeField,它的 to_python 方法会将 value 转换成 Python 的 datetime.time 对象。
  • validate:验证经过转换的 value 是否合法,如果不合法,需要抛出 ValidationError 异常。Field 中实现的 validate 方法只是简单地对 required 属性限制的条件进行验证,如果 required 为 True,且 value 为空值的时候,则会抛出异常。
  • run_validators:这个方法会执行当前实例中包含的验证器,如果出现错误,则会抛出 ValidationError 异常。


所以从上面 clean 方法的源码分析来看,只要去实现 clean 中调用的方法,就能够实现自定义数据转换成 Python 对象和数据的校验。

2) 实现自定义BookField字段

那么接下来我们就亲自去实现一个自定字段 BookField 字段,该字段的功能是输入书籍的 id 后,可以获取 Book 的实例对象,它的代码如下所示:

  1. class BookField(forms.Field):
  2. default_error_message={
  3. 'invalid':'Enter a whole number',
  4. 'not_exist':'Book Not Exist',
  5. }
  6. def to_python(self,num):
  7. try:
  8. num=int(str(num).strip())
  9. return Book.objects.get(id=num)
  10. except (ValueError,TypeError):
  11. raise ValidationError(self.error_messages['invalid'],code='invalid')
  12. except Exception:
  13. raise ValidationError(self.error_messages['not_exist'],code='not_exist')

在编写代码的过程中,我们一定要善于参考 Django Field 类的源码,首先理解定义内置字段的过程,然后照葫芦画瓢,实现自定义 Field 字段。BookField 字段继承自 Field 基类,它重写了 to_python 方法,把 num 当做主键去查询 Book 实例,然后将其作实例对象返回。如下所示:

  1. In [1]: from index.forms import BookField
  2. In [2]: x=BookField()
  3. In [3]: x.clean(1)
  4. Out[3]: <Book: title: Python Django pub:PubName object (8) price:59.00>

3) 继承自forms.CharField实现自定义字段

继承基类 Field 去自定义表单字段可能考虑比较多的问题,所以通常自定义 Field 都会继承自 CharField 或者 IntergerField 等内置字段,即 Field 的子类。举一个简单的例子,给字符串添加固定的前缀词语,如下所示:

  1. class AddstrField(forms.CharField):
  2. def clean(self,value):
  3. return 'C语言中文网 %s'% super().clean(value)

AddstrField 该字段在 clean 方法中使用 super 方法调用了父类的 clean(),也就是使用了 CharField 的数据校验方法,这样就很大程度上简化了该功能实现过程,实例演示如下所示:

  1. In [1]: from index.forms import AddstrField
  2. In [2]: x=AddstrField()
  3. In [3]: x.clean('hello')
  4. Out[3]: 'C语言中文网 hello'

4) 使用ValidationError验证器校验数据

验证器(validators)是一个可以调用的对象,接受一个参数,并验证参数是否符合预期,如果不符合预期就会抛出 ValidationError 异常。现在我们就使用验证器来校验数据的合法性,首先编写一个验证器,如下所示:

  1. #自定义一个验证偶数的验证器,否则抛出异常
  2. def even_validator(value):
  3. if value % 2 !=0:
  4. raise ValidationError('%d is not a even number'% value)
  5. #编写 EvenField字段,只可以接受偶数,否则抛出异常ValidationError
  6. class EvenField(forms.IntegerField):
  7. #使用构造函数__init__ 对其进行初始化,并添加验证器规则
  8. def __init__(self,**kwargs):
  9. super().__init__(validators=[even_validator],**kwargs)

实例演示如下所示:

  1. In [1]: from index.forms import EvenField
  2. In [2]: x=EvenField()
  3. In [3]: x.clean("1")
  4. ....
  5. ValidationError: ['1 is not a even number']
  6. In [5]: x.clean(2)
  7. Out[5]: 2

本节我们讲解 Django Form 表单的自定义字段的实现方法。通过本节的学习,你可能会领略到读源码的重要性,善于学习的人,总会找到合适的方法去学习自己想要掌握的知识,在下一节中,我们将讲解 Django Form 表单如何实现自定义验证规则。

posted @ 2022-08-02 16:02  随遇而安==  阅读(159)  评论(0编辑  收藏  举报