Django——form元件is_valid校驗機制
阿新 • • 發佈:2018-11-16
#先來歸納一下整個流程
#(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}