Django——form組件

以前咱們已經學習了HTML中的基本標籤——form表單,咱們常用它向後臺提交數據,常規的form表單是這樣的:html

<form action="" method="post">
    <p>用戶名:<input type="text" name="user"></p>
    <p>密碼:<input type="text" name="pwd"></p>
    <P><input type="submit"></P>
</form>

可是有許多時候咱們這樣使用很是不方便,爲何這樣說了,上面的form表單中只有兩個提交項,前端

可是實際中確定不止兩個,若是使用上面的方式,那麼一旦有哪個提交項不符合規定,整個form表單都得重寫,git

這是後用戶確定會抱怨,「憑什麼錯了一個所有得重寫」,因此這樣不方便,還有一個就是在對輸入的東西值進行校驗的時候,正則表達式

每次都得先從前面取值,再在後臺校驗,煩不煩,能不能在後臺生成得時候就設置好了?數據庫

這就是jango中form組件能夠很是好的解決得問題。下面會一步一步解開解開它得面紗。django

 

 

1、在Django中構建一個from表單json

下面建立一個最簡單的form表單:數組

from django.shortcuts import render
#導入forms組件
from django import forms

class LoginForm(forms.Form):
    user = forms.CharField(max_length=10)
    pwd = forms.CharField()

def login(request):
    #實例化一個對象
    form_obj = LoginForm()
    return render(request,"login.html",{"form_obj":form_obj})

在前端頁面中展現form_obj:瀏覽器

{{ form_obj.as_p }}

as_p是一個特殊的屬性,常見的有:app

  • {{ form.as_table }} 以表格的形式將它們渲染在<tr> 標籤中
  • {{ form.as_p }} 將它們渲染在<p> 標籤中
  • {{ form.as_ul }} 將它們渲染在<li> 標籤中

 

下面是效果:

能夠看一下前端頁面:

能夠看到它自動給你生成了兩個input標籤,可是還缺一個提交按鈕,form組件能不能作了,答案是不能,還得你本身寫。

剛纔展現了一種前端加載得方法,還有另一種比較靈活得方法,不過須要咱們添加更多的標籤。

<form action="" novalidate method="post">
    {% csrf_token %}
    <div>
        <label for="">用戶名</label>
        {{ form_obj.user }}
    </div>
    <div>
        <label for="">密碼</label>
        {{ form_obj.pwd }}
    </div>
    <input type="submit">
</form>

獲得得效果就是:

貌似如今還看不出來form組件得做用,不急咱們慢慢來。

注意:

(1)novalidate這個屬性是關閉瀏覽器的自動校驗。

(2)Django 原生支持一個簡單易用的跨站請求僞造的防禦。當提交一個啓用CSRF 防禦的POST 表單時,你必須使用上面例子中的csrf_token 模板標籤

 

2、form組件的兩大功能展現

1.保存以前的輸入值

def login(request):
    #若是進行的是提交操做
    if request.method == "POST":
        #再次實例化一個對象,可是與get請求不一樣的是,這裏form_obj是有值。
        form_obj = LoginForm(request.POST)
        return render(request, "login.html", {"form_obj": form_obj})
    #實例化一個對象
    form_obj = LoginForm()
    return render(request,"login.html",{"form_obj":form_obj})

注意:兩個form_obj是不同的,一個是帶着值進行渲染的,一個是空值,這也是爲何提交不會清除掉原來的值得緣由了。

 

2.校驗

Form的實例具備一個is_valid()方法,它會對輸入的字段進行校驗,若是全部的字段都合法,那麼他就會:

  • 返回True
  • 將表單的數據放到cleaned_data屬性中。

在建立類的時候,咱們其實能夠給這個屬性加上約束,就像這樣:

user = forms.CharField(max_length=10,min_length=5)
#最大長度爲10,最小長度爲5,這樣就能夠校驗了

From實例的對象還有一個errors方法,是全部字段全部約束的錯誤信息,咱們能夠將其打印出來:

def login(request):
    #若是進行的是提交操做
    if request.method == "POST":
        #再次實例化一個對象,可是與get請求不一樣的是,這裏form_obj是有值。
        form_obj = LoginForm(request.POST)
        if form_obj.is_valid():
            return HttpResponse("登陸成功")
        else:
            print(form_obj.errors)
            print(type(form_obj.errors))
            print(form_obj.errors["user"])
            print(type(form_obj.errors["user"]))
            return render(request, "login.html", {"form_obj": form_obj})
    #實例化一個對象
    form_obj = LoginForm()
    return render(request,"login.html",{"form_obj":form_obj})

下面是打印結果:

print(form_obj.errors)
#這是個什麼東西咱們不知道
<ul class="errorlist"><li>user<ul class="errorlist"><li>Ensure this value has at least 5 characters (it has 3).</li></ul></li></ul>

#打印一下類型
print(type(form_obj.errors))
#能夠猜想出它是一個錯誤字典
<class 'django.forms.utils.ErrorDict'>

#既然是字典,那麼每一個屬性確定就是鍵了,取出一個鍵
print(form_obj.errors["user"])
#這是什麼咱們也不知道,可是其中有英文報錯
#最少5個,你只有3個
<ul class="errorlist"><li>Ensure this value has at least 5 characters (it has 3).</li></ul>

#打印一下類型
print(type(form_obj.errors["user"]))
#能夠看到是一個列表,列表中的是各類約束的報錯信息
<class 'django.forms.utils.ErrorList'>

#在這裏無論有幾個報錯信息,咱們只取一個就夠了,若是還有錯誤,接着改就是了,這也符合現實狀況。
print(form_obj.errors["user"][0])
#成功取出了報錯信息
Ensure this value has at least 5 characters (it has 3).

既然這樣,在前端就能夠顯示了:

<div>
        <label for="">用戶名</label>
        {{ form_obj.user }}<span>{{ form_obj.user.errors.0 }}</span>
 </div>

顯示效果就是:

可是是英文的,可不能夠是中文了?固然能夠:

在屬性的約束中有一個error_messages,可讓咱們自行進行約束:

class LoginForm(forms.Form):
    user = forms.CharField(max_length=10,min_length=5,
                           error_messages={
                               "min_length":"最小長度爲5",
                               "max_length":"最大長度爲10",
                               "required":"不能爲空",  #不能爲空
                           })

結果以下:

能不能跳一跳樣式了》這樣很差看,這個後面再說,如今還有一個問題,密碼問題,

咱們發現輸入的密碼不能隱藏。

之因此一直沒有舉例,是由於文本輸入有Charfield,並無Passwordfield,在這裏須要藉助widget這個約束進行密碼輸入。

先進行導入:

from django.forms import widgets

而後設計類的時候調用:

    pwd = forms.CharField(
        error_messages={
            "required":"不能爲空",
        },
        widget=widgets.PasswordInput()
    )

效果以下:

在widgets中有不少限制,不是隻有PasswordInput這一個,還有TextInput、CheckboxInput、NumberInput等。

在widgets中還能夠進行屬性的設置,例如:

    pwd = forms.CharField(
        error_messages={
            "required":"不能爲空",
        },
        widget=widgets.PasswordInput(attrs={"class":"active","id":"s1"})
    )

這樣就能夠自行設計樣式了,在前端查看:

 

 

 

3、高級進階:鉤子的設定

不少時候,咱們想要自行設計校驗的手段,該若是作了,Django給咱們提供了鉤子這一手段:

先來看一段源碼:

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

這段源碼是可以設置鉤子的來源。

#局部鉤子
    def clean_user(self):
        val1 = self.cleaned_data.get("user")
        #若是這個字符串所有都是由數組組成
        if not val1.isdigit():
            return val1
        else:
            # 注意這個報錯信息已經肯定了
            raise ValidationError("用戶名不能所有是數字組成")

還能夠設置全局鉤子:

    def clean(self):
        pwd=self.cleaned_data.get("pwd")
        repeat_pwd=self.cleaned_data.get("repeat_pwd")
        if pwd==repeat_pwd:
            print("yes")
            return self.cleaned_data
        else:
            print("no")
            raise ValidationError("兩次密碼不一致!")

爲何有全局鉤子了,由於每個鉤子都是和某個具體的字段綁定,只能獲取本身的字段值,不能獲取其餘的值,因此須要全局鉤子。

from django.shortcuts import render,HttpResponse

# Create your views here.

from django import forms
from django.core.exceptions import NON_FIELD_ERRORS, ValidationError
from django.forms import widgets
import re
from appo1 import models

class LoginForm(forms.Form):
    user = forms.CharField(min_length=6,max_length=12,
                           error_messages={
                               "required":"用戶名不能爲空",
                               "min_length":"最小長度爲5",
                               "max_length":"最大長度爲12"
                           },
                           widget=widgets.TextInput(attrs={"id":"s1"})
                           )
    
    pwd = forms.CharField(min_length=6,
        error_messages={
            "required":"密碼不能爲空",
            "min_length":"最小長度爲6"
        }
    )

    repeat_pwd = forms.CharField(
        error_messages={
            "required": "必須輸入",
        }
    )
    
    email = forms.EmailField(
        error_messages={
            "required": "",
            "invalid":"格式錯誤"
        }
    )
    
    phone = forms.CharField(
        error_messages={
            "required": "必須輸入",
        }
    )

    #必須以clear起手,不然不能盡心檢測
    def clean_user(self):
        import re
        val1 = self.cleaned_data.get("user")
        ret = re.findall(r'^_', val1)

        if not val1.isdigit():
            if ret:
                return val1
            else:
                raise ValidationError("必須如下劃線開頭")
        else:
            raise ValidationError("用戶名不能所有爲數字組成")

    def clean_pwd(self):
        val2=self.cleaned_data.get("pwd")
        if not val2.isdigit():
            return val2
        else:
            raise ValidationError("密碼不能所有爲數字!")

    def clean_phone(self):
        val3 = self.cleaned_data.get("phone")
        if val3.isdigit():
            if len(val3) != 11:
                raise ValidationError("親,號碼是11位!")
            else:
                return val3
        else:
            raise ValidationError("號碼必須是數字!")

    def clean(self):
        pwd=self.cleaned_data.get("pwd")
        repeat_pwd=self.cleaned_data.get("repeat_pwd")
        if pwd==repeat_pwd:
            print("yes")
            return self.cleaned_data
        else:
            print("no")
            raise ValidationError("兩次密碼不一致!")




def login(request):
    if request.method == "POST":
        
        form_obj = LoginForm(request.POST)
        if form_obj.is_valid():
            user = request.POST.get("user")
            pwd  = request.POST.get("pwd")
            email = request.POST.get("email")
            phone = request.POST.get("phone")
            ret = models.user_info.objects.create(user=user,pwd=pwd,email=email,phone=phone)
            return HttpResponse("你好,歡迎回來!")
        else:

            ret = form_obj.errors.get("__all__")
            #{{form_obj.repwd}} < span > {{form_obj.errors.repwd.0}}{{form_obj.non_field_errors.0}} < / span >
            #還能夠在前端直接傳
            return render(request, "login.html", {"form_obj": form_obj,"ret":ret})
    
    
    form_obj = LoginForm()
    return render(request,"login.html",{"form_obj":form_obj})

import json
def user_check(request):
    response = {"is_reg": False}
    user = request.POST.get("user")
    ret = models.user_info.objects.filter(user=user)
    print(ret)
    if ret:
        response["is_reg"] = True
綜合實例1

 

 

4、form組件補充知識

1.Django內置字段以下:

Field
    required=True,               是否容許爲空
    widget=None,                 HTML插件
    label=None,                  用於生成Label標籤或顯示內容
    initial=None,                初始值
    help_text='',                幫助信息(在標籤旁邊顯示)
    error_messages=None,         錯誤信息 {'required': '不能爲空', 'invalid': '格式錯誤'}
    show_hidden_initial=False,   是否在當前插件後面再加一個隱藏的且具備默認值的插件(可用於檢驗兩次輸入是否一直)
    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類型
    ...
View Code

你能夠在裏面選擇屬性的類型以及約束。

 

2.Django內置插件

TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
View Code

在witgits中選擇使用

 

3.經常使用插件選擇

# 單radio,值爲字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# )
 
# 單radio,值爲字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.RadioSelect
# )
 
# 單select,值爲字符串
# user = fields.CharField(
#     initial=2,
#     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# )
 
# 單select,值爲字符串
# user = fields.ChoiceField(
#     choices=((1, '上海'), (2, '北京'),),
#     initial=2,
#     widget=widgets.Select
# )
 
# 多選select,值爲列表
# user = fields.MultipleChoiceField(
#     choices=((1,'上海'),(2,'北京'),),
#     initial=[1,],
#     widget=widgets.SelectMultiple
# )
 
 
# 單checkbox
# user = fields.CharField(
#     widget=widgets.CheckboxInput()
# )
 
 
# 多選checkbox,值爲列表
# user = fields.MultipleChoiceField(
#     initial=[2, ],
#     choices=((1, '上海'), (2, '北京'),),
#     widget=widgets.CheckboxSelectMultiple
# )
View Code
相關文章
相關標籤/搜索