#先来归纳一下整个流程
#(1)首先is_valid()起手,看seld.errors中是否值,只要有值就是flase
#(2)接着分析errors.里面判断_errors是都为空,如果为空返回self.full_clean(),否则返回self._errors
#(3)现在就要看full_clean(),是何方神圣了,里面设置_errors和cleaned_data这两个字典,一个存错误字段,一个存储正确字段。
#(4)在full_clean最后有一句self._clean_fields(),表示校验字段
#(5)在_clean_fields函数中开始循环校验每个字段,真正校验字段的是field.clean(value),怎么校验的不管
#(6)在_clean_fields中可以看到,会将字段分别添加到_errors和cleaned_data这两个字典中
#(7)结尾部分还设置了钩子,找clean_XX形式的,有就执行。执行错误信息也会添加到_errors中
#(8)整个校验过程完成
#下面分析form组件中is_valid校验的流程
#在分析过程中重点关注_erroes和clean_data这两个字典
def login(request): if request.method == "POST": form_obj = LoginForm(request.POST) if form_obj.is_valid(): #如果检验全部通过 print(form_obj.clean_data) #这里全部都没问题 return HttpResponse("你好,欢迎回来!") else: #print(form_obj.clean_data) #print(form_obj.errors) return render(request, "login.html", {"form_obj": form_obj,) form_obj = LoginForm() return render(request, "login.html", {"form_obj": form_obj})
个人代码实例
def register(request): if request.is_ajax(): print('request.POST',request.POST) form = UserForm(request.POST) # 把值传入form表单中 print('form', form) response = {"user": None, "msg": None} if form.is_valid(): response["user"] = form.cleaned_data.get("user") # 生成一条用户纪录 user = form.cleaned_data.get("user") print("user", user) pwd = form.cleaned_data.get("pwd") email = form.cleaned_data.get("email") avatar_obj = request.FILES.get("avatar") extra = {} if avatar_obj: extra["avatar"] = avatar_obj UserInfo.objects.create_user(username=user, password=pwd, email=email, **extra) print('UserInfo',UserInfo) else: print('form.cleaned_data',form.cleaned_data) print('form.errors',form.errors) response["msg"] = form.errors print('response', response) return JsonResponse(response) form = UserForm() print('form = UserFrom', form) return render(request, "register.html", {"form": form})
个人视图
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"> <script src="/static/js/jquery-3.2.1.min.js"></script> <style> #avatar_img { margin-left: 20px; } #avatar { display: none; } .error { color: red; } </style> </head> <body> <h3>注册页面</h3> <div class="container"> <div class="row"> <div class="col-md-6 col-lg-offset-3"> <form id="form"> {% csrf_token %} {% for field in form %} <div class="form-group"> <label for="{{ field.auto_id }}">{{ field.label }}</label> {{ field }} <span class="error pull-right"></span> </div> {% endfor %} <div class="form-group"> <label for="avatar"> 头像 <img id="avatar_img" width="60" height="60" src="/static/blog/img/default.png" alt=""> </label> <input type="file" id="avatar" name="avatar"> </div> <input type="button" class="btn btn-default reg_btn" value="submit"><span class="error"></span> </form> </div> </div> </div> <script> // 头像预览 $("#avatar").change(function () { // 获取用户选中的文件对象 var file_obj = $(this)[0].files[0]; // 获取文件对象的路径 var reader = new FileReader(); reader.readAsDataURL(file_obj); // 修改img的src属性 ,src=文件对象的路径 reader.onload = function () { $("#avatar_img").attr("src", reader.result) }; }); // 基于Ajax提交数据 $(".reg_btn").click(function () { //console.log($("#form").serializeArray()); var formdata = new FormData(); var request_data = $("#form").serializeArray(); console.log(request_data); $.each(request_data, function (index, data) { formdata.append(data.name, data.value) }); formdata.append("avatar", $("#avatar")[0].files[0]); console.log('formdata',formdata); $.ajax({ url: "", type: "post", contentType: false, processData: false, data: formdata, success: function (data) { //console.log(data); if (data.user) { // 注册成功 location.href="/login/" } else { // 注册失败 //console.log(data.msg) // 清空错误信息 $("span.error").html(""); $(".form-group").removeClass("has-error"); // 展此次提交的错误信息! $.each(data.msg, function (field, error_list) { console.log(field, error_list); if (field=="__all__"){ $("#id_re_pwd").next().html(error_list[0]).parent().addClass("has-error"); } $("#id_" + field).next().html(error_list[0]); $("#id_" + field).parent().addClass("has-error"); }) } } }) }) </script> </body> </html>
钩子讲理(非自己)
#钩子代码实例 def clean_user(self): val1 = self.cleaned_data.get("user") #从正确的字段字典中取值 #如果这个字符串全部都是由数组组成 if not val1.isdigit(): return val1 else: # 注意这个报错信息已经确定了 raise ValidationError("用户名不能全部是数字组成") #在校验的循环中except ValidationError as e:,捕捉的就是这个异常 #所以能将错误信息添加到_errors中 #代码分析部分 def is_valid(self): """ Returns True if the form has no errors. Otherwise, False. If errors are being ignored, returns False. 如果表单没有错误,则返回true。否则为假。如果错误是被忽略,返回false。 """ return self.is_bound and not self.errors #is_bound默认有值 #只要self.errors中有一个值,not True = false,返回的就是false def errors(self): """ Returns an ErrorDict for the data provided for the form 返回一个ErrorDict在form表单存在的前提下 """ if self._errors is None: self.full_clean() return self._errors def full_clean(self): """ Cleans all of self.data and populates self._errors and self.cleaned_data. 清除所有的self.data和本地的self._errors和selif.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() def _clean_fields(self): #每个form组件实例化的过程中都会创建一个fields。fields实质上是一个字典。 #储存着类似{"user":"user规则","pwd":"pwd的规则对象"} for name, field in self.fields.items(): #name是你调用的一个个规则字段,field是调用字段的规则 #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. value_from_datadict()从数据字典中获取数据。 每个部件类型知道如何找回自己的数据,因为有些部件拆分数据在几个HTML字段。 """ #现在假设第一个字段是user 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) #filed是一个对象,field.clean才是真正的规则校验 else: #你不是文件的时候怎么校验 #实际中也是走的这一部,value是你输入的字段值 #如果没有问题,那么原样返回 value = field.clean(value) #如果一旦出现问题,那么就会走except中的代码 self.cleaned_data[name] = value if hasattr(self, 'clean_%s' % name): #这里找是否有clean_XX这个名字存在 value = getattr(self, 'clean_%s' % name)() #如果有执行这个函数 self.cleaned_data[name] = value #而在钩子中必须报错的返回值是确定的 #如果上面有问题,就又把错误添加到了_error中 #上面这三行代码是我们能添加钩子的原因,而且规定了钩子名的格式 #如果这个值是正确的话,就会给这个字典添加一个键值对 #刚才在full_clean中self.cleaned_data = {}已经初始化了。 #{”pws“:123} except ValidationError as e: self.add_error(name, e) #如果出现错误,就会给_error这个字典添加一个键值对 #至于add_error这个函数如何添加这个键值对的,我们先不管 #键就是name,值就是错误信息e #在full_clean中已经初始化self._errors = ErrorDict() #假设现在user有问题,那么_error就是这样{”user“:e}