Django源码剖析(05)的更多思考--Form的使用

在前面一篇中大致的分析了Form的源码以及metaclassForm类中的使用, 源码的分析一是阅读优秀代码是如何写的, 学习思想; 二是写出比以前更加易读, 简洁的代码, 减少我们自己所造的轮子.

1. 基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class RetErrorForm(forms.Form):
@property
def errors(self):
base_errors = super(RetErrorForm, self).errors
if not base_errors:
return base_errors
base_errors["ret"] = 0
return base_errors


class LoginForm(RetErrorForm):
username = forms.CharField(max_length=100, required=True, validators=[validate_username_exit])
password = forms.CharField(max_length=100, required=True)


def validate_username_exit(value):
if not User.objects.filter(username=value):
raise ValidationError(
"您尚未注册"
)

validate_username_exit函数应该独立成一个单独的模块儿, 这里为了方便就全部写在一起.
RetErrorForm重载errors属性的原因是因为在返回中我们通常会添加一个标志位, 比如表单验证失败ret = 0, 成功则返回1, 这样前端更加容易判断一些.
对单个字段的数据验证我们可以直接在写在Field类为我们提供的validators参数列表中. 那么上面的表单使用可以说是非常通用的, 大多数的表单验证也仅仅就是这样.

2. 字段数据处理

有这样的一个场景: 用户传递过来的原始数据在经过验证后需要进行加工, 比如添加一个标志位或者其他乱七八糟的, 反正就是要加工之后才能传递给view层使用. 这样我们就可以实现一个clean_ + field_name方法来对数据进行加工, 该方法Form会自动的调用.
再来看一些_clean_fields的部分源码:

1
2
3
4
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value

可以看到在进行了cleaned_data字典赋值以后, 相继的调用我们自己写的clean_方法, 那么:

1
2
3
4
5
6
7
8
class RegisterForm(RetErrorForm):
phone_number = forms.CharField(max_length=11, required=True)
code = forms.CharField(max_length=100, required=True)

def clean_phone_number(self):
# 直接使用self.cleaned_data[phone_number]来获取数据
phone_number = self.cleaned_data[phone_number]
return phone_number + "identify"

函数返回的值上面儿的方法会帮我们进行赋值, 所以也就不用管了.

3. 多字段验证

考虑到这样的情况, 在某些敏感API中, 我们需要保证接口调用的安全性, 那么可以使用对字段进行md5加密后进行验证的手段来做这件事情.
假如我们的注册接口需要对手机号的md5进行验证, 那么就可以这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class RegisterForm(RetErrorForm):
phone_number = forms.CharField(max_length=11, required=True)
code = forms.CharField(max_length=100, required=True)

def clean(self):
cleaned_data = super(RegisterForm, self).clean()
self.validate_code(cleaned_data)

def validate_code(self, cleaned_data):
username = cleaned_data["phone_number"]
code = cleaned_data["code"]
username_hash = hashlib.md5(username).hexdigest()
if username_hash != code:
self.add_error("code", "code传入错误")

我们已经知道了Form.clean()方法会返回一个字典, 称为cleaned_data, 这个字典已经是完全处理过后的数据了, 也就是说假如我们的实例函数中还有一个clean_phone_number方法来对phone_number进行一些处理的话, cleaned_data中为处理后的数据. 本来在validate_code方法中完全可以使用self.data["phone_number"]来获取数据, 但是data其实是原始数据, 而cleaned_data为处理后的数据. 为了避免不必要的错误, 还是使用处理后的数据较为安全.