HTML表單
在HTML中,表單是<form>...</form> 之間元素的集合,它們允許訪問者輸入文本、選擇選項、操作對象和控制等等,然后將信息發送回服務器。
某些表單的元素 —— 文本輸入和復選框 —— 非常簡單而且內建於HTML 本身。其它的表單會復雜些;例如彈出一個日期選擇對話框的界面、允許你移動滾動條的界面、使用JavaScript 和CSS 以及HTML 表單<input> 元素來實現操作控制的界面。與<input> 元素一樣,一個表單必須指定兩樣東西:
- 目的地:響應用戶輸入數據的URL
- 方式:發送數據所使用的HTTP 方法
例如,Django Admin 站點的登錄表單包含幾個<input> 元素:type="text" 用於用戶名,type="password" 用於密碼,type="submit" 用於“Log in" 按鈕。它還包含一些用戶看不到的隱藏的文本字段,Django 使用它們來決定下一步的行為。
它還告訴瀏覽器表單數據應該發往<form> 的action 屬性指定的URL —— /admin/,而且應該使用method 屬性指定的HTTP 方法 —— post。
當觸發<input type="submit" value="Log in"> 元素時,數據將發送給/admin/。
Django 中的表單
Django 的Form 類
表單系統的核心部分是Django 的Form 類。Django 的模型描述一個對象的邏輯結構、行為以及展現給我們的方式,與此類似,Form 類描述一個表單並決定它如何工作和展現。
就像模型類的屬性映射到數據庫的字段一樣,表單類的字段會映射到HTML 的<input>表單的元素。
表單字段在瀏覽器中呈現給用戶的是一個HTML 的“widget” —— 用戶界面的一個片段。每個字段類型都有一個合適的默認Widget 類,需要時可以覆蓋。
widget 部件
實例化、處理和渲染表單
在Django 中渲染一個對象時,我們通常:
- 在視圖中獲得它(例如,從數據庫中獲取)
- 將它傳遞給模板上下文
- 使用模板變量將它擴展為HTML 標記
在模板中渲染表單和渲染其它類型的對象幾乎一樣,除了幾個關鍵的差別。
構建一個表單
在html里構建這里就不寫了
1.在app下面新建一個存放Forn類的文件
例如:myforms.py
#!/usr/bin/env python #-*- coding:utf-8 -*- from django import forms from django.forms import fields class UserForm(forms.Form): user = fields.CharField( max_length=18, min_length=6, required = True, error_messages={ 'required':'用戶輸入為空', 'max_length':'用戶輸入太長了', 'min_length':'用戶輸入太短了' }) #自定義錯誤顯示信息 pwd = fields.CharField(required=True,min_length=8) age = fields.IntegerField(required=True) email = fields.EmailField(required=True,min_length=8, error_messages={ 'invalid':"用戶輸入格式錯誤,只要是格式錯誤都用invalid" })
注:它不包含 <form> 標簽和提交按鈕。我們必須自己在模板中提供它們。
要操作一個通過URL發布的表單,我們要在視圖中實例表單。
2.views.py
from django.shortcuts import render from django.shortcuts import redirect from app01.myfoms import UserForm # Create your views here. def users(request): if request.method == "GET": obj = UserForm() #這就相當於自動生成了一個html return render(request,'fm.html',{'obj':obj}) elif request.method == 'POST': fo = UserForm(request.POST) # print(fo) #前端提交過來的數據 v = fo.is_valid() # 判斷發過來的數據驗證是否通過 if v: print(fo.cleaned_data) # 查看用戶提交的數據 return redirect('http://www.baidu.com') # 成功驗證跳轉到百度 else: print(fo.errors) # 查看相應提交數據的錯誤信息 return render(request, 'fm.html', {"obj": fo}) return render(request, 'fm.html')
3.url
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^users/$', views.users), ]
4.模板html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/users/" method="POST" novalidate> <p>{{ obj.user }}{{ obj.errors.user.0 }} </p> <p>{{ obj.pwd }}{{ obj.errors.pwd.0 }}</p> <p>{{ obj.age }}{{ obj.errors.age.0 }}</p> <p>{{ obj.email }}{{ obj.errors.email.0 }}</p> <input type="submit" value="提交"> </form> /* obj.error.user.0 是顯示在輸入框后顯示錯誤的顯示信息 */ </body> </html>
提示:
form類實例化的方法和屬性 is_valid() 方法 判斷表單提交是否合法的數據結構,返回True/False cleaned_data 屬性中找到所有合法的表單數據 轉換好為Python 的字典類型。
使用表單模板
例如:
1新建一個表單的py文件 例如:myForms.py 在app下面新建表單內容,
內容如下
#!/usr/bin/env python #-*- coding:utf-8 -*- from django import forms from django.forms import fields class UserInfo(forms.Form): user = fields.CharField() age = fields.IntegerField()
2.視圖views.py
from django.shortcuts import render from app01 import myForms # Create your views here. def index(request): form = myForms.UserInfo() if request.method == 'GET': return render(request,'index.html',{'form':form})
3.路由urls.py
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^index/', views.index), ]
4.模板html
通過視圖把fom表單渲染到模板的html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/index" method="POST"> <table border="1">{{ form.as_table }}</table> {{ form.as_p }} <ul> {{ form.as_ul }} </ul> </form> </body> </html>
上面用了幾個表單渲染,form.as_p 和 forms.as_ul 和 forms.as_table
表單渲染選項
表單模板的額外標簽
不要忘記,表單的輸出不 包含<form> 標簽,和表單的submit 按鈕
對於<label>/<input> 對,還有幾個輸出選項:
- {{ form.as_table }} 以表格的形式將它們渲染在<tr> 標簽中
- {{ form.as_p }} 將它們渲染在<p> 標簽中
- {{ form.as_ul }} 將它們渲染在<li> 標簽中
注意,你必須自己提供<table> 或<ul> 元素。
表單字段詳解
在django的form表單api,每個字段都是一個類,所有的字段都繼承Field這個類。
提示:多看源碼
Field這個類的常用參數
Field required=True, 是否允許為空 widget=None, HTML插件 注:重點 label=None, 用於生成Label標簽或顯示內容 類似HTML的label的標簽 initial=None, 初始值 (<input type="text" value="xixi"> 類似html的input框的value值) help_text='', 幫助信息(在標簽旁邊顯示) error_messages=None, 錯誤信息 {'required': '不能為空', 'invalid': '格式錯誤'} show_hidden_initial=False, 是否在當前插件后面再加一個隱藏的且具有默認值的插件(可用於檢驗兩次輸入是否一直) validators=[], 自定義驗證規則 localize=False, 是否支持本地化 disabled=False, 是否可以編輯 label_suffix=None Label內容后綴
常用字段
ChoiceField 選擇類型的字段(例如,下拉框,等等) choice[(1,'北京'),(2,'上海'),(3,'深圳')] 這個選項是提供下拉框的內容 表單模板的額外標簽 不要忘記,表單的輸出不 包含<form> 標簽,和表單的submit 按鈕 對於<label>/<input> 對,還有幾個輸出選項: {{ form.as_table }} 以表格的形式將它們渲染在<tr> 標簽中 {{ form.as_p }} 將它們渲染在<p> 標簽中 {{ form.as_ul }} 將它們渲染在<li> 標簽中 注意,你必須自己提供<table> 或<ul> 元素。 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 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='', 幫助提示 注:choices選項:由可迭代的二元組組成(比如[(A, B), (A, B) ...]),用來給這個字段提供選擇項。如果設置了 choices ,默認表格樣式就會顯示選擇框,而不是標准的文本框,而且這個選擇框的選項就是 choices 中的元組。 每個元組中的第一個元素,是存儲在數據庫中的值;第二個元素是該選項更易理解的描述 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類型 ...
重點form字段
Form重點: - 字段 用於保存正則表達式 ChoiceField ***** MultipleChoiceField CharField IntegerField DecimalField DateField DateTimeField EmailField GenericIPAddressField FileField RegexField
表單字段的HTML插件
所有Field字段本質上封裝了兩個東西,一個是正則表達式,一個是HTML插件
每個表單字段都有一個對應的Widget 類,它對應一個HTML 表單Widget,例如<input type="text">。
在大部分情況下,字段都具有一個合理的默認Widget。例如,默認情況下,CharField 具有一個TextInput Widget,它在HTML 中生成一個<input type="text">。
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
常用widget的選擇HTML插件
不要將Widget 與表單字段搞混淆。表單字段負責驗證輸入並直接在模板中使用。Widget 負責渲染網頁上HTML 表單的輸入元素和提取提交的原始數據。但是,Widget 需要賦值給表單字段。
# 單radio,值為字符串 widgets方式 # user = fields.CharField( # initial=2, # widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) # ) # 單radio,值為字符串 ChoiceField選擇字段方式 # 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 # )
提示:根據自已喜好,選擇用widget插件,還是ChoiceField方式
widget只能生成form相關的html標簽,不能生成div,span標簽等等。。。。
數據實時更新
方式一:(推薦)
在使用選擇標簽時,需要注意choices的選項可以從數據庫中獲取,但是由於是類屬性字段 ***獲取的值無法實時更新***,
那么需要自定義構造方法(__init__)從而達到此目的。
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'].widget.choices = ((1, '上海'), (2, '北京'),) # 或 self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')
方式二:
依賴數據庫的model的__str__方法
from django import forms from django.forms import fields from django.forms import widgets from django.forms.models import ModelChoiceField class FInfo(forms.Form): authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) to_field_name='id' #使用id的那列字段作為input框的value值
FORM自定制匹配正則表達式的擴展
方式一
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( error_messages={'invalid': '...'}, validators=[RegexValidator(r'^[0-9]+$', '請輸入數字'), RegexValidator(r'^159[0-9]+$', '數字必須以159開頭')], )
字段下的validator
validators=[RegexValidator(r'^[0-9]+$', '請輸入數字')]
validators 自定義匹配正則表達式,可以有多個匹配的正則表達式,匹配順序是從前到后
RegexValidator這個類有兩個參數,第一個是正則表達式,第二個錯誤提示
方式二
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.RegexField(r'^[0-9]+$',error_messages={'invalid': '...'})
正則字段
基於源碼流程的擴展
如果某一個字段不僅只是自定義正則表達式匹配,例如:還有一些用戶注冊的唯一性等等要求,這時候就要基於form源碼來擴展
單子段驗證
重寫:clean_字段名方法
from django import forms from django.forms import fields from django.forms import widgets from django.core.exceptions import NON_FIELD_ERRORS, ValidationError class AForm(forms.Form): username = fields.CharField() user_id = fields.IntegerField( widget=widgets.Select(choices=[(0,'張三'),(1,'李四'),(2,'老王'),]) ) # 自定義方法 clean_字段名 # 必須返回值self.cleaned_data['username'] # 如果出錯:raise ValidationError('用戶名已存在') def clean_username(self): v = self.cleaned_data['username'] if models.UserInfo.objects.filter(username=v).count(): # 整體錯了 # 自己詳細錯誤信息 raise ValidationError('用戶名已存在') return v def clean_user_id(self): return self.cleaned_data['user_id']
整體錯誤驗證
自定義clean方法
class AForm(forms.Form): username = fields.CharField() user_id = fields.IntegerField( widget=widgets.Select(choices=[(0,'張三'),(1,'李四'),(2,'老王'),]) ) # 自定義方法 clean_字段名 # 必須返回值self.cleaned_data['username'] # 如果出錯:raise ValidationError('用戶名已存在') def clean_username(self): v = self.cleaned_data['username'] if models.UserInfo.objects.filter(username=v).count(): # 整體錯了 # 自己詳細錯誤信息 raise ValidationError('用戶名已存在') return v def clean_user_id(self): return self.cleaned_data['user_id'] def clean(self): value_dict = self.cleaned_data v1 = value_dict.get('username') v2 = value_dict.get('user_id') if v1 == 'root' and v2==1: raise ValidationError('整體錯誤信息') return self.cleaned_data