TML表單是網站交互性的經典方式。 本章將介紹如何用Django對用戶提交的表單數據進行處理。
HTTP 請求
HTTP協議以"請求-回復"的方式工作。客戶發送請求時,可以在請求中附加數據。服務器通過解析請求,就可以獲得客戶傳來的數據,并根據URL來提供特定的服務。
GET 方法
我們在之前的項目中創建一個 search.py 文件,用于接收用戶的請求:
/HelloWorld/HelloWorld/search.py 文件代碼:
# -*- coding: utf-8 -*-fromdjango.httpimportHttpResponsefromdjango.shortcutsimportrender_to_response# 表單defsearch_form(request): returnrender_to_response('search_form.html')# 接收請求數據defsearch(request): request.encoding='utf-8'if'q'inrequest.GET: message='你搜索的內容為: ' + request.GET['q']else: message='你提交了空表單'returnHttpResponse(message)
在模板目錄 templates 中添加 search_form.html 表單:
/HelloWorld/templates/search_form.html 文件代碼:
<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>菜鳥教程(runoob.com)</title></head><body><formaction="/search"method="get"><inputtype="text"name="q"><inputtype="submit"value="搜索"></form></body></html>
urls.py 規則修改為如下形式:
/HelloWorld/HelloWorld/urls.py 文件代碼:
fromdjango.conf.urlsimporturlfrom . importview,testdb,searchurlpatterns=[url(r'^hello$', view.hello), url(r'^testdb$', testdb.testdb), url(r'^search-form$', search.search_form), url(r'^search$', search.search),]
訪問地址 http://127.0.0.1:8000/search-form 并搜索,結果如下所示:
POST 方法
上面我們使用了GET方法。視圖顯示和請求處理分成兩個函數處理。
提交數據時更常用POST方法。我們下面使用該方法,并用一個URL和處理函數,同時顯示視圖和處理請求。
我們在tmplate 創建 post.html:
/HelloWorld/tmplates/post.html 文件代碼:
<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>菜鳥教程(runoob.com)</title></head><body><formaction="/search-post"method="post"> {% csrf_token %} <inputtype="text"name="q"><inputtype="submit"value="Submit"></form><p>{{ rlt }}</p></body></html>
在模板的末尾,我們增加一個 rlt 記號,為表格處理結果預留位置。
表格后面還有一個{% csrf_token %}的標簽。csrf 全稱是 Cross Site Request Forgery。這是Django提供的防止偽裝提交請求的功能。POST 方法提交的表格,必須有此標簽。
在HelloWorld目錄下新建 search2.py 文件并使用 search_post 函數來處理 POST 請求:
/HelloWorld/HelloWorld/search2.py 文件代碼:
# -*- coding: utf-8 -*-fromdjango.shortcutsimportrenderfromdjango.views.decoratorsimportcsrf# 接收POST請求數據defsearch_post(request): ctx={} ifrequest.POST: ctx['rlt']=request.POST['q']returnrender(request, "post.html", ctx)
urls.py 規則修改為如下形式:
/HelloWorld/HelloWorld/urls.py 文件代碼:
fromdjango.conf.urlsimporturlfrom . importview,testdb,search,search2urlpatterns=[url(r'^hello$', view.hello), url(r'^testdb$', testdb.testdb), url(r'^search-form$', search.search_form), url(r'^search$', search.search), url(r'^search-post$', search2.search_post),]
訪問 http://127.0.0.1:8000/search-post 顯示結果如下:
完成以上實例后,我們的目錄結構為:
HelloWorld|-- HelloWorld| |-- __init__.py| |-- __init__.pyc| |-- search.py| |-- search.pyc| |-- search2.py| |-- search2.pyc| |-- settings.py| |-- settings.pyc| |-- testdb.py| |-- testdb.pyc| |-- urls.py| |-- urls.pyc| |-- view.py| |-- view.pyc| |-- wsgi.py| `-- wsgi.pyc |-- TestModel | |-- __init__.py | |-- __init__.pyc | |-- admin.py | |-- admin.pyc | |-- apps.py | |-- migrations | | |-- 0001_initial.py | | |-- 0001_initial.pyc | | |-- __init__.py | | `-- __init__.pyc| |-- models.py| |-- models.pyc| |-- tests.py| `-- views.py |-- db.sqlite3 |-- manage.py `-- templates |-- base.html |-- hello.html |-- post.html `-- search_form.html
Request 對象
每個 view 函數的第一個參數是一個 HttpRequest 對象,就像下面這個 hello() 函數:
from django.http import HttpResponsedef hello(request): return HttpResponse("Hello world")
HttpRequest對象包含當前請求URL的一些信息:
屬性 | 描述 |
path | 請求頁面的全路徑,不包括域名—例如, "/hello/"。 |
CONTENT_LENGTH
CONTENT_TYPE
QUERY_STRING: 未解析的原始查詢字符串
REMOTE_ADDR: 客戶端IP地址
REMOTE_HOST: 客戶端主機名
SERVER_NAME: 服務器主機名
SERVER_PORT: 服務器端口
META 中這些頭加上前綴HTTP_最為Key, 例如: |
HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE
HTTP_HOST: 客戶發送的HTTP主機頭信息
HTTP_REFERER: referring頁
HTTP_USER_AGENT: 客戶端的user-agent字符串
HTTP_X_BENDER: X-Bender頭信息
method | 請求中使用的HTTP方法的字符串表示。全大寫表示。例如: if request.method=='GET': do_something() elif request.method=='POST': do_something_else() |
GET | 包含所有HTTP GET參數的類字典對象。參見QueryDict 文檔。 |
filename: 上傳文件名,用Python字符串表示
content-type: 上傳文件的Content type
content: 上傳文件的原始內容
注意:只有在請求方法是POST,并且請求頁面中<form>有enctype="multipart/form-data"屬性時FILES才擁有數據。否則,FILES 是一個空字典。 | |
POST | 包含所有HTTP POST參數的類字典對象。參見QueryDict 文檔。 服務器收到空的POST請求的情況也是有可能發生的。也就是說,表單form通過HTTP POST方法提交請求,但是表單中可以沒有數據。因此,不能使用語句if request.POST來判斷是否使用HTTP POST方法;應該使用if request.method=="POST" (參見本表的method屬性)。 注意: POST不包括file-upload信息。參見FILES屬性。 |
REQUEST | 為了方便,該屬性是POST和GET屬性的集合體,但是有特殊性,先查找POST屬性,然后再查找GET屬性。借鑒PHP's $_REQUEST。 例如,如果GET={"name": "john"} 和POST={"age": '34'},則 REQUEST["name"] 的值是"john", REQUEST["age"]的值是"34". 強烈建議使用GET and POST,因為這兩個屬性更加顯式化,寫出的代碼也更易理解。 |
COOKIES | 包含所有cookies的標準Python字典對象。Keys和values都是字符串。 |
FILES | 包含所有上傳文件的類字典對象。FILES中的每個Key都是<input type="file" name="" />標簽中name屬性的值. FILES中的每個value 同時也是一個標準Python字典對象,包含下面三個Keys: |
META | 包含所有可用HTTP頭部信息的字典。 例如: |
user | 是一個django.contrib.auth.models.User 對象,代表當前登錄的用戶。 如果訪問用戶當前沒有登錄,user將被初始化為django.contrib.auth.models.AnonymousUser的實例。 你可以通過user的is_authenticated()方法來辨別用戶是否登錄: if request.user.is_authenticated(): # Do something for logged-in users.else: # Do something for anonymous users. 只有激活Django中的AuthenticationMiddleware時該屬性才可用 |
session | 唯一可讀寫的屬性,代表當前會話的字典對象。只有激活Django中的session支持時該屬性才可用。 |
raw_post_data | 原始HTTP POST數據,未解析過。 高級處理時會有用處。 |
Request對象也有一些有用的方法:
方法 | 描述 |
---|---|
__getitem__(key) | 返回GET/POST的鍵值,先取POST,后取GET。如果鍵不存在拋出 KeyError。 這是我們可以使用字典語法訪問HttpRequest對象。 例如,request["foo"]等同于先request.POST["foo"] 然后 request.GET["foo"]的操作。 |
has_key() | 檢查request.GET or request.POST中是否包含參數指定的Key。 |
get_full_path() | 返回包含查詢字符串的請求路徑。例如, "/music/bands/the_beatles/?print=true" |
is_secure() | 如果請求是安全的,返回True,就是說,發出的是HTTPS請求。 |
QueryDict對象
在HttpRequest對象中, GET和POST屬性是django.http.QueryDict類的實例。
QueryDict類似字典的自定義類,用來處理單鍵對應多值的情況。
QueryDict實現所有標準的詞典方法。還包括一些特有的方法:
方法 | 描述 |
---|---|
__getitem__ | 和標準字典的處理有一點不同,就是,如果Key對應多個Value,__getitem__()返回最后一個value。 |
__setitem__ | 設置參數指定key的value列表(一個Python list)。注意:它只能在一個mutable QueryDict 對象上被調用(就是通過copy()產生的一個QueryDict對象的拷貝). |
get() | 如果key對應多個value,get()返回最后一個value。 |
update() | 參數可以是QueryDict,也可以是標準字典。和標準字典的update方法不同,該方法添加字典 items,而不是替換它們: >>> q=QueryDict('a=1')>>> q=q.copy() # to make it mutable>>> q.update({'a': '2'})>>> q.getlist('a') ['1', '2']>>> q['a'] # returns the last['2'] |
items() | 和標準字典的items()方法有一點不同,該方法使用單值邏輯的__getitem__(): >>> q=QueryDict('a=1&a=2&a=3')>>> q.items()[('a', '3')] |
values() | 和標準字典的values()方法有一點不同,該方法使用單值邏輯的__getitem__(): |
此外, QueryDict也有一些方法,如下表:
方法 | 描述 |
---|---|
copy() | 返回對象的拷貝,內部實現是用Python標準庫的copy.deepcopy()。該拷貝是mutable(可更改的) — 就是說,可以更改該拷貝的值。 |
getlist(key) | 返回和參數key對應的所有值,作為一個Python list返回。如果key不存在,則返回空list。 It's guaranteed to return a list of some sort.. |
setlist(key,list_) | 設置key的值為list_ (unlike __setitem__()). |
appendlist(key,item) | 添加item到和key關聯的內部list. |
setlistdefault(key,list) | 和setdefault有一點不同,它接受list而不是單個value作為參數。 |
lists() | 和items()有一點不同, 它會返回key的所有值,作為一個list, 例如: >>> q=QueryDict('a=1&a=2&a=3')>>> q.lists()[('a',['1','2','3'])] |
urlencode() | 返回一個以查詢字符串格式進行格式化后的字符串(e.g., "a=2&b=3&b=5"). |
章來源于公眾號【Python野路子】
HTML表單是網站交互性的經典方式,本章將記錄如何用Django對用戶提交的表單數據進行處理。
站在前端角度,form表單是HTML中用于提交數據給服務器的一個標簽,所有的表單元素(input/textarea/button...)都要放在form標簽當中,還有以下參數:
Django中的表單不是html中的那個表單,這里是指Django中的組件名叫表單,主要做以下2件事:
1)表單驗證數據的合法性。
2)通渲染表單模板;
Form類在from django import forms中,使用時需要定義一個Form的子類,相當于將請求的表單數據封裝到一個特殊的類中,并自動完成一些數據的驗證工作。
1)先在某個應用app下,新建一個forms.py的文件(類似前面學過的views.py和urls.py在對應app應用下)。
2)在里面定義一個表單類,繼承自django.forms.Form
from django import forms
3)在表單類中,創建字段與模型類類似,但是沒有null=True(是否接受空值NULL,默認值False)或者blank=True(是否接受空白內容,默認為False)等這幾種參數了,有的參數是required=True/False(請求能否為空,True不能為空,默認為True)。
from django import forms
class RegisterForm(forms.Form):
# label屬性是form表單中特有的屬性,代表這個字段的描述,這個屬性類似于模型類中的verbose_name屬性
username = forms.CharField(label=u'用戶名', max_length=20, min_length=3)
# 存儲到數據庫的密碼,是一個加密后的字符串,但是這里是通過前端傳輸過來的,并沒進行加密
password = forms.CharField(label=u'密碼', max_length=20, min_length=8)
4)表單生成HTML表單元素。
# views.py
class RegisterView(View):
def get(self, request):
# 如果需要使用django表單渲染html頁面
# 實例化該表單模型,傳遞給前端
form = RegisterForm()
return render(request, 'register_form.html', {'form': form})
def post(self, request):
# 如果不使用django表單,需要一個一個的值取出來,并且需要自己寫對應的驗證
username = request.POST.get('username')
password = request.POST.get('password')
return render(request, 'register_form.html', locals())
<!-- register_form.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% if username %}
提交的post數據:
{{ username }}
{{ password }}
{% else %}
<form action = "{% url "user:register" %}" method="post">
{% csrf_token %}
{{ form }} <!--會自動識別表單屬性的 -->
<input type="submit" vlaue='注冊'>
</form>
{% endif %}
</body>
</html>
注意:使用Django的Form類生成的表單,不包含form和submit按鈕兩個標簽,需要手動添加。
一般用于生成HTML表單元素很少使用,尤其在現在前后分離趨勢下,這個功能很雞肋,把前端該做的事情放到后臺來實現,增加了代碼的耦合性,也增加了服務器的壓力。
我們一般使用forms組件的校驗功能,比如賬號長度必須6~12位,密碼長度必須為8~20位,且必須含大小寫字母,我們可以一個個獲取前端傳過來的字段進行一個個校驗,如果字段比較多,如果一個個單獨校驗,那比較繁瑣,那我們可以使用django提供的forms組件,我們先來看個簡單的。
# forms.py
'''
forms.py的作用
它是專門編寫你的forms配置的模型
forms.py本身命名沒有要求,你可以為任意名稱, 但是我們一般約定它叫forms,代表這個文件是專門處理該APP下處理表單組件的
'''
from django import forms
class RegisterForm(forms.Form):
username = forms.CharField(max_length=20, min_length=3)
password = forms.CharField(max_length=20, min_length=8)
# views.py
class RegisterView(View):
def get(self, request):
# 如果需要使用django表單渲染html頁面
# 實例化該表單模型,傳遞給前端
form = RegisterForm()
return render(request, 'register_form.html', {'form': form})
def post(self, request):
# 滿足Form里面
form = RegisterForm({'username': 'admin', 'password': '12345678'})
print(form.is_valid()) # True
print(form.cleaned_data) # {'username': 'admin', 'password': '12345678'},輸出全部校驗正確的字段
print(form.errors)
# 其中一個不滿足,例如密碼長度少于8
form = RegisterForm({'username': 'admin', 'password': '12345'})
print(form.is_valid()) # False
print(form.cleaned_data) # {'username': 'admin'}, 輸出校驗正確的字段
print(form.errors)
'''
<ul class="errorlist">
<li>password
<ul class="errorlist">
<li>Ensure this value has at least 8 characters (it has 5).</li>
</ul>
</li>
</ul>
'''
print(type(form.errors)) # <class 'django.forms.utils.ErrorDict'>
print(form['password'].errors)
# <ul class="errorlist"><li>Ensure this value has at least 8 characters (it has 5).</li></ul>
print(form.errors.get('password'))
# <ul class="errorlist"><li>Ensure this value has at least 8 characters (it has 5).</li></ul>
print(form.errors.get_json_data())
# {'password': [{'message': 'Ensure this value has at least 8 characters (it has 5).', 'code': 'min_length'}]}
# 比Form少一個字段
form = RegisterForm({'username': 'admin'})
print(form.is_valid()) # False
print(form.cleaned_data) # {'username': 'admin'}
print(form.errors)
'''
<ul class="errorlist">
<li>password
<ul class="errorlist">
<li>This field is required.</li>
</ul>
</li>
</ul>
'''
print(form.errors.get('password'))
# <ul class="errorlist"><li>This field is required.</li></ul>
print(form.errors.get_json_data())
# {'password': [{'message': 'This field is required.', 'code': 'required'}]}
# 比Form多一個字段,例如多個age
form = RegisterForm({'username': 'admin', 'password': '12345678', 'age': 18})
print(form.is_valid()) # True
print(form.cleaned_data) # {'username': 'admin', 'password': '12345678'}, 比Form多的字段不輸出
print(form.errors)
return render(request, 'register_form.html', locals())
我們這里先傳幾個固定的參數來進行測試,通過上面測試,我們可以總結下。
1)使用is_valid()方法可以驗證用戶提交的數據是否合法,這個方法會返回一個bool值,合法則返回True,否則返回False。其中在實例化一個form對象時,傳的參數必須與Form里面定義的字段:
2)cleaned_data()獲取滿足Form校驗規則的字段, 使用cleaned_data必須執行完is_valid()方法。
3)如果表單校驗沒有通過,form對象則會產生一個errors屬性,這個屬性包括所有的驗證錯誤信息。我們可以獲取錯誤信息傳遞給前端。
必須要執行完is_valid函數,否則errors是不會包含錯誤。
在表單中,創建字段跟模型是一模一樣的,但是沒有null=True(是否接受空值NULL,默認值False)或者blank=True(是否接受空白內容,默認為False)等這幾種參數了,有的參數是required=True/False(請求能否為空,True不能為空,默認為True)。
使用Field可以是對數據驗證的第一步。你期望這個提交上來的數據是什么類型,那么就使用什么類型的Field。
CharField
用來接收文本。參數:
EmailField
用來接收郵件,會自動驗證郵箱格式是否合法。錯誤信息的key:required、invalid。
FloatField
用來接收浮點類型,并且如果驗證通過后,會將這個字段的值轉換為浮點類型。參數:
錯誤信息的key:required、invalid、max_value、min_value。
IntegerField
用來接收整形,并且驗證通過后,會將這個字段的值轉換為整形。參數:
錯誤信息的key:required、invalid、max_value、min_value。
URLField
用來接收url格式的字符串。錯誤信息的key:required、invalid。
FileField
用來接收文件,allow_empty_file=False是否允許空文件。
ImageField
接收圖片,注意需要PIL模塊,pip3 install Pillow,以上2個字典使用時,需要注意2點:
Form類中的各個Field字段其實都有一些基本的驗證器,如果表單中的某個字段想要額外添加一些驗證功能,可以指定validators參數給字段添加一些驗證器,或者給這個字段定義一個額外的形如clean_[字段名]自定義驗證方法。
內置驗證器通過字段的validators參數指定對應的驗證器列表即可,Django內置的驗證器都在django.core.validators中,常用的內置驗證器:
系統自帶表單校驗規則,有時無法滿足我們的需求,比如在注冊的表單驗證中,我們需要驗證用戶是否已經被注冊過了,那么這個時候就需要去數據庫查詢判斷才知道,這個時候我們可以對某個字段,進行自定義的驗證。
需要針對某個字段進行特殊驗證時,可以在Form表單類中定義一個clean_[字段名]的方法,就會自動執行這個方法進行驗證了,如果不符合要求,直接拋出異常即可,符合要求則返回對應的值。如果想要針對多個字段之間的驗證,可以重寫clean()方法,當所有字段的驗證都通過后就會執行這個方法。
# form.py
from django import forms
from .models import Account
class RegisterForm(forms.Form):
# label 屬性是form表單中特有的屬性,代表這個字段的描述,這個屬性類似于模型類中的verbose_name屬性
username = forms.CharField(label=u'用戶名', min_length=3, max_length=18,
error_messages={'required': u'用戶名不能為空',
'min_length': u'用戶名不少于3位',
'max_length': u'用戶名不超過18位'
})
password = forms.CharField(label=u'密碼', required=True, min_length=6, max_length=16,
error_messages={'required': u'密碼不能為空', 'min_length': u'密碼不少于6位',
'max_length': u'密碼不超過16位'})
confirm_pwd = forms.CharField(label=u'確認密碼', min_length=6, max_length=16,
error_messages={'required': u'確認密碼不能為空', 'min_length': u'密碼不少于6位',
'max_length': u'密碼不超過16位'})
# 可以不填
email = forms.EmailField(label=u'郵箱', required=False) # required 請求能否為空,True不能為空,默認為True
# 表單自定義錯誤消息:重寫方法clean_field(field是一個屬性名),可以自定義針對某一個field的驗證機制,一個屬性一個對應方法
# clean() 或者 clean_xxx() 會在執行 form.is_valid() 的過程中被調用
def clean_username(self):
# 當字段的基本驗證通過后,會將數據存儲在cleaned_data中
username = self.cleaned_data['username']
if Account.objects.filter(username=username).exists():
# 如果這里判斷有多個錯誤存在,則使用add_error方法。
self.add_error('username', '該用戶名已使用,請重新選擇!')
# 如果只是單個錯誤,使用ValidationError,否則這里raise拋出去了,后面就不能執行了
#raise forms.ValidationError('用戶已存在')
# 敏感詞匯
if username.find('mmp') >= 0:
self.add_error('username', '存在敏感字符')
# 需要返回處理后的值
return username
def clean_confirm_pwd(self): #不能用clean_password,因為加載這個時候,confirm_pwd還沒加載出來,是沒有值的。
pwd = self.cleaned_data['password']
confirm_pwd = self.cleaned_data['confirm_pwd']
if pwd != confirm_pwd:
raise forms.ValidationError('兩次密碼不一致', code='')
return pwd
def clean(self):
# 執行這個方法時表示所有字段都已驗證成功,當然,需要先調用父類的clean()方法
cleaned_data = super().clean()
# 當字段的基本驗證通過后,會將數據存儲在cleaned_data中
# 獲取需要驗證的字段
...
# 需要返回處理后的值
return cleaned_data
def get_error_dict(self):
# 提取錯誤信息
if hasattr(self, 'errors'): #hasattr() 函數用于判斷對象是否包含對應的屬性。
errors = self.errors.get_json_data()
print(type(errors)) # <class 'dict'>
print(errors)
# {'username': [{'message': '用戶名不能為空', 'code': 'required'}],
# 'password': [{'message': '密碼不能為空', 'code': 'required'}],
# 'confirm_pwd': [{'message': '確認密碼不能為空', 'code': 'required'}]}
err_msg_dict = {}
for key, message in errors.items():
print(key, message) # username [{'message': '用戶名不能為空', 'code': 'required'}]
msg_list = []
for msg in message:
print(msg) # {'message': '用戶名不能為空', 'code': 'required'}
msg_list.append(msg['message'])
err_msg_dict[key] = msg_list
print(err_msg_dict) # {'username': ['用戶名不能為空'], 'password': ['密碼不能為空'], 'confirm_pwd': ['確認密碼不能為空']}
return err_msg_dict
return None
def get_error_str(self):
# 提取錯誤信息
if hasattr(self, 'errors'): #hasattr() 函數用于判斷對象是否包含對應的屬性。
errors = self.errors.get_json_data().values() # 獲取字典值部分
print(errors)
#dict_values([[{'message': '用戶名不能為空', 'code': 'required'}],
# [{'message': '密碼不能為空', 'code': 'required'}], [{'message': '確認密碼不能為空', 'code': 'required'}]])
err_msg_list = []
for itme in errors:
print(itme) # [{'message': '用戶名不能為空', 'code': 'required'}]
err_msg_list.append(itme[0].get('message'))
print(err_msg_list) # ['用戶名不能為空', '密碼不能為空', '確認密碼不能為空']
err_msg_str = ';'.join(err_msg_list) # 將錯誤信息通過;拼接成字符串
print(err_msg_str) # 用戶名不能為空;密碼不能為空;確認密碼不能為空
return err_msg_str
return None
視圖views.py
class RegisterView(View):
def get(self, request):
# 如果需要使用django表單渲染html頁面
# 實例化該表單模型,傳遞給前端
return render(request, 'register_form.html')
def post(self, request):
print(request.POST) #<QueryDict: {'username': ['admin'], 'password': ['6666'], 'confirm_pwd': ['222'], 'email': ['']}>
register_form = RegisterForm(request.POST)
if register_form.is_valid():
# 使用cleaned_data 必須執行完is_valid 且返回為True才能獲取數據,保存用戶提交上來的數據
print(register_form.cleaned_data)
username = register_form.cleaned_data['username']
password = register_form.cleaned_data['password']
# 利用字典解包方式
params = {'username': username, 'password': password}
Account.objects.create(**params)
return HttpResponse('注冊成功')
else:
print(register_form.errors)
# <ul class="errorlist"><li>username<ul class="errorlist"><li>用戶名不能為空</li></ul></li></ul>
print(register_form.errors.get_json_data())
# {'username': [{'message': '用戶名不能為空', 'code': 'required'}]}
print(register_form.errors.get('username')) # <ul class="errorlist"><li>用戶名不能為空</li></ul>
print(register_form.errors.get('username').get_json_data()) # [{'message': '用戶名不能為空', 'code': 'required'}]
err_msg_dict = register_form.get_error_dict()
# {'username': ['用戶名不能為空'], 'password': ['密碼不能為空'], 'confirm_pwd': ['確認密碼不能為空']}
return render(request, 'register_form.html', err_msg_dict)
模板register_form.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="{% url "user:register" %}" method="post">
用戶名:<input type="text" name="username"><span>{{ username.0 }}</span>
密 碼:<input type="password" name="password"><span>{{ password.0 }}</span>
確認密碼:<input type="password" name="confirm_pwd"><span>{{ confirm_pwd.0 }}</span>
郵 箱:<input type="email" name="email">
<input type="submit" vlaue='注冊'>
</form>
</body>
</html>
自定義錯誤信息,可以通過在定義表單類是設置相關屬性,校驗不通過則會報默認錯誤。我們可以通過設置error_messages字典來設置對應message。也可以自定義錯誤信息。
不管那種必須要執行完is_valid函數,否則執行相關errors是不會包含錯誤,form類的運行順序是init,clean,validte,save,如果遇到類似錯誤,比如說不能為空,最大最小長度時,在error_messages寫了錯誤信息,也自定義了表單錯誤信息,則required=True時調用error_message,否則自定義的。
通過上面我們發現,Form表單中的字段與模型中的字段基本是一模一樣的,表單中需要驗證的數據,也就是模型中需要保存的,我們可以將模型中的字段與表單中的字段進行綁定,例如上面RegisterForm改成繼承forms.ModelForm。
# models.py
class Account(models.Model):
username = models.CharField(max_length=16)
password = models.CharField(max_length=20)
class Meta:
db_table = 'tb_account'
def __str__(self):
return self.username
# forms.py
class RegisterForm(forms.ModelForm):
"""
1.補充 Model 沒有的 Field 到表單,例如這里的confirm_pwd
2.覆蓋 Model 中的 Field 定義
"""
confirm_pwd = forms.CharField(label=u'確認密碼', min_length=6, max_length=16,
error_messages={'required': u'確認密碼不能為空', 'min_length': u'密碼不少于6位', 'max_length': u'密碼不超過16位'})
class Meta:
model = Account # 對應model中的類
fields = '__all__' # 說明要關聯類中的哪些字段,默認__all__關聯所有字段,如果不需要全部
# exclude = ['avatar'] # 排除字段
error_messages = { # 自定義錯誤信息
'username': {
'max_length': '用戶名長度不超過16位',
'required': "用戶名不能為空",
},
'password': {
'max_length': '用戶名長度不超過16位',
'required': "用戶名不能為空",
}
}
def clean_confirm_pwd(self):
pwd = self.cleaned_data['password']
confirm_pwd = self.cleaned_data['confirm_pwd']
if pwd != confirm_pwd:
raise forms.ValidationError('兩次密碼不一致', code='')
return pwd
# views.py
from .forms import RegisterForm
from hashlib import md5
class RegisterView(View):
def get(self, request):
# 如果需要使用django表單渲染html頁面
# 實例化該表單模型,傳遞給前端
return render(request, 'register_form.html')
def post(self, request):
print(request.POST)
register_form = RegisterForm(request.POST)
if register_form.is_valid():
# 使用cleaned_data 必須執行完is_valid 且返回為True才能獲取數據,保存用戶提交上來的數據
print(register_form.cleaned_data)
# {'username': 'admin666', 'password': '1q2w3e4r', 'confirm_pwd': '1q2w3e4r'}
register_form.save() # 這里save即使多了個confirm_pwd沒關系,是按照model字段來的。
return HttpResponse('注冊成功')
else:
print(register_form.errors.get_json_data())
#{'username': [{'message': '用戶名不能為空', 'code': 'required'}],
# 'password': [{'message': '用戶名不能為空', 'code': 'required'}],
# 'confirm_pwd': [{'message': '確認密碼不能為空', 'code': 'required'}]}
print(register_form.errors.get('username').get_json_data()) # [{'message': '用戶名不能為空', 'code': 'required'}]
return render(request, 'register_form.html')
RegisterForm是繼承自forms.ModelForm,然后在表單中定義了一個Meta類,在Meta類中指定了model=Account,以及fields="__all__",這樣就可以將Account模型中所有的字段都復制過來,進行驗證。如果只想針對其中幾個字段進行驗證,那么可以給fields指定一個列表,將需要的字段寫進去。
如果要驗證的字段比較多,只是除了少數幾個字段不需要驗證,那么可以使用exclude來代替fields。
與普通的Form表單驗證類型類似,ModelForm表單的驗證在調用is_valid()或訪問errors屬性時隱式調用。
驗證時,可以在ORM模型的字段定義中指定validators參數,添加額外的驗證器即可。
自定義錯誤,因為字段都不是在表單中定義的,而是在模型中定義的,因此一些錯誤消息無法在字段中定義。那么這時候可以在Meta類中,定義error_messages,然后把相應的錯誤消息寫到里面去。
自定義校驗規則與Form表單一樣。
save保存,ModelForm還有save方法,可以在驗證完成后直接調用save方法,就可以將這個數據保存到數據庫中了。這個方法必須要在clean沒有問題后才能使用,如果在clean之前使用,會拋出異常。另外,我們在調用save方法的時候,如果傳入一個commit=False,那么只會生成這個模型的對象,而不會把這個對象真正的插入到數據庫中。比如表單上驗證的字段沒有包含模型中所有的字段,這時候就可以先創建對象,再根據填充其他字段,把所有字段的值都補充完成后,再保存到數據庫中,例如:
form = MyForm(request.POST)
if form.is_valid():
article = form.save(commit=False)
article.category = 'Python'
article.save()
return HttpResponse('succes')
else:
print(form.get_errors())
return HttpResponse('fail')
并且即使form的數據比model模型字段多也沒關系。
ModelForm的子類可以接受現有的模型實例作為關鍵字參數instance;如果提供此功能,則save()將更新該實例。如果沒有提供,save() 將創建模型的一個新實例。
# 修改表數據是,記得把instance信息也傳進去,不然是新建數據,而不是對某行數據進行修改。
article_form = ArticleAddForm(dict_data, instance=article) # 指定給誰做修改
if article_form.is_valid():
article_form.save()
return json_status.result(message='文章更新成功')
#Django#
文章來源于公眾號【Python野路子】
eb.py的form模塊能夠幫助你生成HTML表單;獲取用戶的輸入,并在處理或添加到數據庫之前對其進行內容的驗證。
如圖所示:
上圖是一個post表單
提交以后會給code.py中的hello post處理
上圖中
i=web.input()
接收表單中的值,并賦值給i
?
最后返回給模板hello.html中的name屬性
return render.hello(name=i.name)
?
運行效果如下:
此時我們再文本框里面任意輸入一個值,提交以后默認值就是改變(這里的默認值就是我們的name屬性)
*請認真填寫需求信息,我們會在24小時內與您取得聯系。