1. 程式人生 > >015---Django的forms元件

015---Django的forms元件

Form介紹 

我們之前在HTML頁面中利用form表單向後端提交資料時,都會寫一些獲取使用者輸入的標籤並且用form標籤把它們包起來。

與此同時我們在好多場景下都需要對使用者的輸入做校驗,比如校驗使用者是否輸入,輸入的長度和格式等正不正確。如果使用者輸入的內容有錯誤就需要在頁面上相應的位置顯示對應的錯誤資訊.。

Django form元件就實現了上面所述的功能。

總結一下,其實form元件的主要功能如下:

  • 生成頁面可用的HTML標籤
  • 對使用者提交的資料進行校驗
  • 保留上次輸入內容

先在應用目錄下my_forms.py定義好一個UserForm類

 1 from django import forms
2 from django.forms import widgets 3 4 class UserForm(forms.Form): 5 username = forms.CharField(min_length=4, label='使用者名稱', 6 widget=widgets.TextInput(attrs={"class": "form-control"}), 7 error_messages={
8 "required": "使用者名稱不能為空", 9 }) 10 pwd = forms.CharField(min_length=4, label='密碼', 11 error_messages={ 12 "required": "密碼不能為空", 13 },
14 widget=widgets.PasswordInput(attrs={"class": "form-control"})) 15 r_pwd = forms.CharField(min_length=4, label='確認密碼', 16 widget=widgets.PasswordInput(attrs={"class": "form-control"}), 17 error_messages={ 18 "required": "密碼不能為空", 19 }) 20 email = forms.EmailField(label='郵箱', 21 widget=widgets.EmailInput(attrs={"class": "form-control"}), 22 error_messages={ 23 "required": '郵箱不能為空', 24 "invalid": "郵箱格式錯誤", 25 }) 26 tel = forms.CharField(label='手機號', 27 widget=widgets.TextInput(attrs={"class": "form-control"}), 28 )

再寫一個檢視函式:

 1 在寫一個檢視函式
 2     def reg(request):
 3         form = UserForm()
 4         if request.method == "POST":
 5             print(request.POST)
 6             # 例項化form物件的時候,把post提交過來的資料直接傳進去
 7             form = UserForm(request.POST)  # form表單的name屬性值應該與forms元件的欄位名稱一致
 8             if form.is_valid():
 9                 print(form.cleaned_data)
10                 return HttpResponse('註冊成功')
11         return render(request, 'reg.html', locals())

login.html

 2     <!DOCTYPE html>
 3     <html lang="zh_CN">
 4     <head>
 5         <meta charset="UTF-8">
 6         <meta http-equiv="x-ua-compatible" content="IE=edge">
 7         <meta name="viewport" content="width=device-width, initial-scale=1">
 8         <title>註冊</title>
 9     </head>
10     <body>
11 
12     <h3>傳統form表單</h3>
13     <form action="" method="post">
14         {% csrf_token %}
15         <p>使用者名稱:<input type="text" name="username"></p>
16         <p>密碼:<input type="password" name="pwd"></p>
17         <p>確認密碼:<input type="password" name="r_pwd"></p>
18         <p>郵箱:<input type="email" name="email"></p>
19         <p>手機號:<input type="tel" name="tel"></p>
20         <p><input type="submit" value="提交"></p>
21     </form>
22 
23     <h3>forms元件渲染方式1</h3>
24     <form action="" method="post" novalidate>
25         {% csrf_token %}
26         <p>{{ form.username.label }}:{{ form.username }} <span>{{ form.username.errors.0 }}</span></p>
27         <p>密碼:{{ form.pwd }}
28             <span>{{ form.pwd.errors.0 }}</span></p>
29         <p>確認密碼:{{ form.r_pwd }}
30             <span>{{ form.r_pwd.errors.0 }}</span></p>
31         <p>郵箱:{{ form.email }}
32             <span>{{ form.email.errors.0 }}</span></p>
33         <p>手機號:{{ form.tel }}
34             <span>{{ form.tel.errors.0 }}</span></p>
35         <p><input type="submit" value="提交"></p>
36     </form>
37 
38      <h3>forms元件渲染標籤方式2</h3>
39         <form action="" method="post" novalidate>
40             {% csrf_token %}
41             {% for field in form %}
42                 <div class="form-group clearfix">
43                     <label for="">{{ field.label }}</label>
44                     {{ field }}
45                     <span style="color: red" class="pull-right">{{ field.errors.0 }}</span>
46                     {% if field.name == 'r_pwd' %}
47                         <span style="color: red" class="pull-right">{{ errors.0 }}</span>
48                     {% endif %}
49                 </div>
50             {% endfor %}
51             <input type="submit" value="註冊" class="btn btn-default pull-right">
52         </form>
53 
54 
55 
56     <h3>forms元件渲染標籤方式3   不推薦使用</h3>
57     <form action="" method="post">
58         {% csrf_token %}
59         {{ form.as_p }}
60         <input type="submit" value="註冊">
61     </form>
62     </body>
63     </html>

看網頁效果發現 也驗證了form的功能:• 前端頁面是form類的物件生成的                                      -->生成HTML標籤功能• 當用戶名和密碼輸入為空或輸錯之後 頁面都會提示        -->使用者提交校驗功能• 當用戶輸錯之後 再次輸入 上次的內容還保留在input框   -->保留上次輸入內容

Form那些事兒

常用欄位與外掛

建立Form類時,主要涉及到 【欄位】 和 【外掛】,欄位用於對使用者請求資料的驗證,外掛用於自動生成HTML;

initial

初始值,input框裡面的初始值。

複製程式碼
class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="使用者名稱",
        initial="張三"  # 設定預設值
    )
    pwd = forms.CharField(min_length=6, label="密碼")
複製程式碼

error_messages

重寫錯誤資訊。

複製程式碼
class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="使用者名稱",
        initial="張三",
        error_messages={
            "required": "不能為空",
            "invalid": "格式錯誤",
            "min_length": "使用者名稱最短8位"
        }
    )
    pwd = forms.CharField(min_length=6, label="密碼")
複製程式碼

password

複製程式碼
class LoginForm(forms.Form):
    ...
    pwd = forms.CharField(
        min_length=6,
        label="密碼",
        widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
    )
複製程式碼

radioSelect

單radio值為字串

複製程式碼
class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="使用者名稱",
        initial="張三",
        error_messages={
            "required": "不能為空",
            "invalid": "格式錯誤",
            "min_length": "使用者名稱最短8位"
        }
    )
    pwd = forms.CharField(min_length=6, label="密碼")
    gender = forms.fields.ChoiceField(
        choices=((1, ""), (2, ""), (3, "保密")),
        label="性別",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )
複製程式碼

單選Select

複製程式碼
class LoginForm(forms.Form):
    ...
    hobby = forms.fields.ChoiceField(
        choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ),
        label="愛好",
        initial=3,
        widget=forms.widgets.Select()
    )
複製程式碼

多選Select

複製程式碼
class LoginForm(forms.Form):
    ...
    hobby = forms.fields.MultipleChoiceField(
        choices=((1, "籃球"), (2, "足球"), (3, "雙色球"), ),
        label="愛好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )
複製程式碼

單選checkbox

複製程式碼
class LoginForm(forms.Form):
    ...
    keep = forms.fields.ChoiceField(
        label="是否記住密碼",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )
複製程式碼

多選checkbox

複製程式碼
class LoginForm(forms.Form):
    ...
    hobby = forms.fields.MultipleChoiceField(
        choices=((1, "籃球"), (2, "足球"), (3, "雙色球"),),
        label="愛好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )
複製程式碼

關於choice的注意事項:

在使用選擇標籤時,需要注意choices的選項可以從資料庫中獲取,但是由於是靜態欄位 ***獲取的值無法實時更新***,那麼需要自定義構造方法從而達到此目的。

方式一:

複製程式碼
from django.forms import Form
from django.forms import widgets
from django.forms import fields

 
class MyForm(Form):
 
    user = fields.ChoiceField(
        # choices=((1, '上海'), (2, '北京'),),
        initial=2,
        widget=widgets.Select
    )
 
    def __init__(self, *args, **kwargs):
        super(MyForm,self).__init__(*args, **kwargs)
        # self.fields['user'].choices = ((1, '上海'), (2, '北京'),)
        #
        self.fields['user'].choices = models.Classes.objects.all().values_list('id','caption')
複製程式碼

方式二:

複製程式碼
from django import forms
from django.forms import fields
from django.forms import models as form_model

 
class FInfo(forms.Form):
    authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())  # 多選
    # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())  # 單選
複製程式碼

Django Form所有內建欄位

複製程式碼
Field
    required=True,               是否允許為空
    widget=None,                 HTML外掛
    label=None,                  用於生成Label標籤或顯示內容
    initial=None,                初始值
    help_text='',                幫助資訊(在標籤旁邊顯示)
    error_messages=None,         錯誤資訊 {'required': '不能為空', 'invalid': '格式錯誤'}
    validators=[],               自定義驗證規則
    localize=False,              是否支援本地化
    disabled=False,              是否可以編輯
    label_suffix=None            Label內容字尾
 
 
CharField(Field)
    max_length=None,             最大長度
    min_length=None,             最小長度
    strip=True                   是否移除使用者輸入空白
 
IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
FloatField(IntegerField)
    ...
 
DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             總長度
    decimal_places=None,         小數位長度
 
BaseTemporalField(Field)
    input_formats=None          時間格式化   
 
DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
DurationField(Field)            時間間隔:%d %H:%M:%S.%f
    ...
 
RegexField(CharField)
    regex,                      自定製正則表示式
    max_length=None,            最大長度
    min_length=None,            最小長度
    error_message=None,         忽略,錯誤資訊使用 error_messages={'invalid': '...'}
 
EmailField(CharField)      
    ...
 
FileField(Field)
    allow_empty_file=False     是否允許空檔案
 
ImageField(FileField)      
    ...
    注:需要PIL模組,pip3 install Pillow
    以上兩個字典使用時,需要注意兩點:
        - form表單中 enctype="multipart/form-data"
        - view函式中 obj = MyForm(request.POST, request.FILES)
 
URLField(Field)
    ...
 
 
BooleanField(Field)  
    ...
 
NullBooleanField(BooleanField)
    ...
 
ChoiceField(Field)
    ...
    choices=(),                選項,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               外掛,預設select外掛
    label=None,                Label內容
    initial=None,              初始值
    help_text='',              幫助提示
 
 
ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查詢資料庫中的資料
    empty_label="---------",   # 預設空顯示內容
    to_field_name=None,        # HTML中value的值對應的欄位
    limit_choices_to=None      # ModelForm中對queryset二次篩選
     
ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
 
     
TypedChoiceField(ChoiceField)
    coerce = lambda val: val   對選中的值進行一次轉換
    empty_value= ''            空值的預設值
 
MultipleChoiceField(ChoiceField)
    ...
 
TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   對選中的每一個值進行一次轉換
    empty_value= ''            空值的預設值
 
ComboField(Field)
    fields=()                  使用多個驗證,如下:即驗證最大長度20,又驗證郵箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
 
MultiValueField(Field)
    PS: 抽象類,子類中可以實現聚合多個字典去匹配一個值,要配合MultiWidget使用
 
SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
FilePathField(ChoiceField)     檔案選項,目錄下檔案顯示在頁面中
    path,                      資料夾路徑
    match=None,                正則匹配
    recursive=False,           遞迴下面的資料夾
    allow_files=True,          允許檔案
    allow_folders=False,       允許資料夾
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支援的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1時候,可解析為192.0.2.1, PS:protocol必須為both才能啟用
 
SlugField(CharField)           數字,字母,下劃線,減號(連字元)
    ...
 
UUIDField(CharField)           uuid型別
複製程式碼

校驗

方式一:

複製程式碼
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
 
class MyForm(Form):
    user = fields.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')],
    )
複製程式碼

方式二:

複製程式碼
import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError
 
 
# 自定義驗證規則
def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手機號碼格式錯誤')
 
 
class PublishForm(Form):
    title = fields.CharField(max_length=20,
                            min_length=5,
                            error_messages={'required': '標題不能為空',
                                            'min_length': '標題最少為5個字元',
                                            'max_length': '標題最多為20個字元'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': '標題5-20個字元'}))
 
    # 使用自定義驗證規則
    phone = fields.CharField(validators=[mobile_validate, ],
                            error_messages={'required': '手機不能為空'},
                            widget=widgets.TextInput(attrs={'class': "form-control",
                                                          'placeholder': u'手機號碼'}))
 
    email = fields.EmailField(required=False,
                            error_messages={'required': u'郵箱不能為空','invalid': u'郵箱格式錯誤'},
                            widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'郵箱'}))
複製程式碼

 方式三:

 1     def clean(self):
 2         pwd = self.cleaned_data.get('pwd')
 3         r_pwd = self.cleaned_data.get('r_pwd')
 4         print(pwd, r_pwd)
 5         if pwd and r_pwd:
 6             if pwd == r_pwd:
 7                 return self.cleaned_data
 8             raise ValidationError('兩次密碼不一致')
 9         else:
10             return self.cleaned_data
11 
12     def clean_username(self):
13         val = self.cleaned_data.get('username')
14         user = UserInfo.objects.filter(name=val)
15         if not user:
16             return val
17         else:
18             raise ValidationError("使用者名稱已註冊")
19 
20     def clean_tel(self):
21         val = self.cleaned_data.get('tel')
22         if len(val) == 11:
23             return val
24         raise ValidationError('手機號格式錯誤')

 總結:

 1  '''
 2     區域性鉤子:
 3         1、is_valid()
 4