1. 程式人生 > >form元件, ModelForm元件

form元件, ModelForm元件

一:form元件功能
  form 元件:
1.效驗
2.頁面顯示錯誤資訊
3.渲染頁面
4.只要錯誤資訊重置

 

二: form元件的語法:

fm=BookForm({"title":"yuan","price":123,"email":"123"})
fm.is_valid()
# 如果效驗失敗就返回False
# 如果效驗成功就返回True
fm.cleaned_data  返回校成功的資料
{對的資料的鍵值:對的資料的值}
fm.errors    返回效驗錯誤的資料,後面是列表
{'錯的鍵值':['錯誤資訊',]}
必須先is_valid 才能取出對的和錯的資料

 

注意:

 

  form表單裡的所有欄位必須給值,如果不給值就會報錯,但是如果給的多就不會報錯,

  clean-data裡只有效驗成功的欄位

 

三.form 元件應用

例項:註冊使用者

 模型:model.py

class UserInfo(models.Model):
    user=models.CharField(max_length=32)
    pwd=models.CharField(max_length=32)
    email=models.CharField(max_length=32)

 

 form元件: (寫在哪個裡都可以)

from django.forms import widgets(需要匯入)
class UserForm(forms.Form):
    msg = {"required": '該欄位不能為空', "invalid": "格式錯誤", "min_length": "長度不可以小於五"}  # invalid只適用於郵箱
    # error_messages  自定義錯誤資訊,label  自定義lable顯示文字
    user = forms.CharField(min_length=5,
                           error_messages
=msg, label='使用者名稱', widget=widgets.TextInput(attrs={"class": "form-control"})) pwd = forms.CharField(min_length=5, error_messages=msg, label='密碼', widget=widgets.PasswordInput(attrs={"class": "form-control"})) r_pwd=forms.CharField(error_messages=msg, label='確認密碼', widget=widgets.PasswordInput(attrs={"class": "form-control"})) email = forms.EmailField(error_messages=msg, label='郵箱', widget=widgets.EmailInput(attrs={"class": "form-control"}))

 檢視函式:

 

方式一:

 views,py裡 

def reg(request):
    if request.method=="GET":
        return render(request,'reg.html')
    if request.method=="POST":
        # 資料校驗
        # 直接把它放進來做校驗就可以request裡多一個crsf-token也沒事多餘一個也不會報錯
        form=UserFrom(request.POST)
        if form.is_valid():
            print(form.cleaned_data)
        else:
            print("來自errorcleaned",form.cleaned_data)
            print("哈哈",form.errors)
            # 因為後面是列表所以需要
            # print('哈哈1',form.errors.get('user')[0])
            error=form.errors
            return render(request,"reg.html",{'error':error})

 

對應的模板裡HTML

 # novalidate   讓瀏覽器對你放行不進行攔截
    <form action="" method="post" novalidate>
        {% csrf_token %}
        <p>使用者名稱<input type="text" name="user"></p><span class="error">{{ error.user.0 }}</span>
        <p>密碼<input type="password" name="'pwd"></p><span class="error">{{ error.pwd.0 }}</span>
        <p>郵箱<input type="text" name="email"></p><span class="error">{{ error.email.0 }}</span>
        <p><input type="submit"></p>
    </form>

 

 

 方式二:

views.py  

def reg(request):

    if request.method=="POST":
        form = UserFrom(request.POST)
        if form.is_valid():
            print(form.cleaned_data)
        else:
            error = form.errors
            return  render(request,'reg.html',locals())

    form=UserFrom()
    return render(request, "reg.html", locals())

 

 模板:

# 不能對應加錯誤
<form action="" method="post">
    {% csrf_token %}
    {{ form.as_p }}

    <p><input type="submit"></p>
</form>

 

方式三:

模板:

方式三直接傳過來,對的不會重置
<form action="" method="post" novalidate>
    {% csrf_token %}
    <p>使用者名稱{{ form.user }}</p><span class="error">{{ error.user.0 }}</span>
    <p>密碼{{ form.pwd }}</p><span class="error">{{ error.pwd.0 }}</span>
    <p>郵箱{{ form.email }}</p><span class="error">{{ error.email.0 }}</span>
    <p><input type="submit"></p>
</form>

 

 方式四:

模板:

{#方式四#}
{#用最後這個就行#}
<form action="" method="post" novalidate>
    {% csrf_token %}

    {% for field in form %}
        <div>
        <lable for=''>{{ field.label }}</lable>
        {{ field }}<span class="error">{{ field.errors.0 }}</span>
        </div>
    {% endfor %}
    <div><input type="submit">提交</div>
</form>

 field就是:{{form.user}}

 

 

 

form 元件的使用流程:

原始碼解析is_valid(self)#self是自定義元件物件

 self._errors = ErrorDict()

如果不加鉤子就結束了,如果加鉤子繼續:

程式碼:
        form=UserForm(request.POST)
        
        類的例項化:實際上是完成一次賦值,  
            self.field={"user":user,'pwd':pwd,"email":email}
                    後面的值都是類例項化出來的物件,是各自的規則
                     user=forms.CharField(min_length=5,
                         label="使用者名稱",
                         error_messages=msg,
                         widget=widgets.TextInput(attrs={"class":"form-control"})
                         )
                        pwd=forms.CharField(error_messages=msg,
                                               label="密碼",
                                              widget=widgets.PasswordInput(attrs={"class":"form-control"})
                                               )
                        email=forms.EmailField(error_messages={"invalid":"郵箱格式錯誤"},
                                               label="郵箱",
                                               widget=widgets.EmailInput(attrs={"class":"form-control"})
                                               )
                            form.is_valid()
                           
    self._clean_fields() #效驗欄位                       
    #效驗資料
        
        
        def _clean_fields(self):
        
        #field是各自的規則,name是欄位字串
        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.
            # UserForm({"user":"yuan",'pwd':123,'email':"123"})
            if field.disabled:
                value = self.get_initial_for_field(field, name)
            else:
                # value就是 yuan 等
                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:
                
                    #field是校驗規則,value是  yuan 如果校驗不成功直接跳到except裡
                    value = field.clean(value)
                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)

 

 

 

# 定義鉤子第一層校驗如果失敗了,區域性的鉤子就不執行了
    def clean_user(self):
        val = self.cleaned_data.get('user')
        print(val)
        # user_obj = auth.authenticate(username=val)
        from django.contrib.auth.models import User
        user_obj=User.objects.filter(username=val).first()

        print("haha",user_obj)
        if user_obj:
            raise ValidationError('使用者名稱已存在')

        else:
            return val

    def clean_pwd(self):
        pwd = self.cleaned_data.get('pwd')
        if pwd.isdigit():
            raise ValidationError('密碼是純數字')

        else:
            return pwd

    def clean_email(self):
        import re
        email = self.cleaned_data.get('email')
        if re.search('[0-9a-zA-Z][email protected]\.com', email):
            return email
        else:
            raise ValidationError('請輸入格式化為@163.com郵箱')
   # 全域性勾的錯誤的鍵為"__all___"
    def clean(self):
        pwd = self.cleaned_data.get("pwd")
        r_pwd = self.cleaned_data.get("r_pwd")
        print(r_pwd)
        if pwd and r_pwd:
            if pwd == r_pwd:
                return self.cleaned_data
            else:
                raise ValidationError('兩次密碼不一致!')
        else:
            return self.cleaned_data

 可以給他起個別名,省著顯示錯誤資訊得時候還得判斷:

def clean(self):
        password =  self.cleaned_data.get('password')
        r_pwd= self.cleaned_data.get('r_pwd')
        print(password,r_pwd)

        if password==r_pwd:
            print('haha')
            return self.cleaned_data
        else:
            self.add_error('r_pwd',ValidationError('密碼不一致'))