您好,登錄后才能下訂單哦!
上一節(jié)我們講完了ModelAdmin的使用, 但是在操作中也發(fā)現(xiàn), 新增編輯會(huì)員時(shí), 我們無法驗(yàn)證數(shù)據(jù)是否正確, 比如
這些要求, 我們就必須得使用自定義的表單來完成了
項(xiàng)目地址:https://gitee.com/ccnv07/django_example
通過表單, 我們可以實(shí)現(xiàn)以下的功能
關(guān)于表單的代碼我們一般放在每個(gè)模塊的forms.py中
CharField
單行文本輸入字段, 對(duì)應(yīng)模型的CharField字段
表單中的樣式就是input type=textmax_length
: 最大長度min_length
: 最小長度strip
: 是否過濾左右的空格empty_value
: 為空時(shí)的值, 默認(rèn)是空字符串
EmailField
郵箱輸入文本字段, 對(duì)應(yīng)模型的EmailField字段
標(biāo)案中是input type=text
但是會(huì)自動(dòng)增加一個(gè)郵箱格式的校驗(yàn)
ChoiceField
下拉單選字段, 這個(gè)在模型中是沒有的
對(duì)應(yīng)表單的select標(biāo)簽choices
參數(shù)也是二維元組的格式
BooleanField
選擇字段, 對(duì)應(yīng)表單中的checkbox
DateField
日期選擇字段input_formats
: 定義時(shí)間格式, 默認(rèn)是:
['%Y-%m-%d', # '2006-10-25'
'%m/%d/%Y', # '10/25/2006'
'%m/%d/%y'] # '10/25/06'
DateTimeField
日期時(shí)間字段, 同DateField
TimeField
時(shí)間字段, 同DateField
DecimalField
十進(jìn)制數(shù)字字段max_value
: 最大值min_value
: 最小值max_digits
: 前置0被去除后的最大位數(shù)decimal_places
: 允許的小數(shù)位的長度
FileField
文件上傳字段
IntegerField
整數(shù)字段max_value
: 最大值min_value
: 最小值
剩下還有很多表單字段類型, 在之后的教程中再繼續(xù)介紹, 目前這些, 就算是比較常見的字段了
通過介紹以后, 大家發(fā)現(xiàn), 我沒說最常見的PasswordField, 其實(shí)django的表單中就沒有這個(gè)字段類型, 那么, 如何實(shí)現(xiàn)密碼字段呢?
password = forms.CharField(widget=forms.PasswordInput(),max_length=12,min_length=6, strip=True,
help_text='編輯時(shí)為空則不更改密碼')
通過制定widget參數(shù)為forms.PasswordInput(), 就可以實(shí)現(xiàn)密碼字段了
所以其實(shí)并不是Form類來規(guī)定表單字段的類型, 而是widget來實(shí)現(xiàn)的表單字段的類型, 對(duì)于每種表單字段的類型, django都有對(duì)應(yīng)的模板, 通過字段的參數(shù), 生成對(duì)應(yīng)的html代碼
label
: 表單字段的label標(biāo)簽的名稱widget
: 指定此字段采用的字段樣式類型help_text
: 字段的幫助文本
只有ModelForm才有元類, Form是不需要元類的
以下是一個(gè)元類的例子
from django.forms import ModelForm
class Meta:
model = Account
# 使用自定義的Form, 就必須指定fields or exclude屬性, 否則報(bào)錯(cuò)
fields = ('account', 'password', 'email', 'phone', 'status')
error_messages = {
NON_FIELD_ERRORS: {
'unique_together': "%(model_name)s's %(field_labels)s are not unique.",
}
}
model
: 指定ModelForm綁定的模型fields
: 指定后臺(tái)新增編輯時(shí)要顯示的字段error_messages
: 指定通用的錯(cuò)誤信息文案
其他還有一些復(fù)雜的操作, 會(huì)在之后的教程中詳細(xì)講解。現(xiàn)在我們的一個(gè)表單就完成了
但是如果想讓表單在后臺(tái)中生效, 就需要把表單綁定到ModelAdmin上
from django.contrib import admin
from .forms import AccountForm
@admin.register(Account)
class AccountAdmin(admin.ModelAdmin):
form = AccountForm
在ModelAdmin中指定form參數(shù), 就可以把表單綁定上去了。 點(diǎn)擊新增/編輯頁面, 也就可以看到表單生效了
接下來, 我們就需要完成表單提交數(shù)據(jù)以后的驗(yàn)證的方法了
其實(shí)在定義表單字段時(shí), 我們就已經(jīng)完成了一部分的驗(yàn)證了
比如
account = forms.CharField(
required=True, error_messages={
'required': '請(qǐng)輸入用戶名',
}, label='用戶名')
就定義了account字段必須填寫, 如果出錯(cuò)則返回“請(qǐng)輸入用戶名”的提示
但是這個(gè)并不能完成我們所有的驗(yàn)證, 所以我們也可以根據(jù)字段進(jìn)行自定義的驗(yàn)證
比如, 我要實(shí)現(xiàn)account用戶名字段是唯一的
from django import forms
from django.core.exceptions import ValidationError
from .models import Account
class AccountForm(forms.ModelForm):
...省略代碼
def clean_account(self):
_info = Account.objects.filter(account=self.cleaned_data['account'],
is_deleted=0).values('id')
if _info:
raise ValidationError('用戶已存在')
return self.cleaned_data['account']
當(dāng)我們執(zhí)行form.is_valid()方法進(jìn)行驗(yàn)證時(shí), django的form類會(huì)依次執(zhí)行clean_字段名的自定義驗(yàn)證方法, 如果有拋出異常(raise ValidationError('用戶已存在')), 則中斷并返回錯(cuò)誤, 否則讀取到clean_字段名的方法的返回值, 并且寫入到cleaned_data這個(gè)字典中
根據(jù)同樣的方法, 我們也可以寫出來對(duì)email和phone字段的驗(yàn)證
以上已經(jīng)基本能實(shí)現(xiàn)我們的功能了, 但是在后臺(tái)的操作中, 新增和編輯用的是同一個(gè)表單, 如果我們需要針對(duì)新增和編輯的不同場景, 進(jìn)行一些不同的操作, 就比較麻煩了。
所以我們需要通過在ModelAdmin中, 對(duì)Form進(jìn)行一定的操作, 似的Form可以完成更多的判斷
# account/admin.py
class AccountAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(AccountAdmin, self).get_form(request, obj=obj, **kwargs)
# obj 保存的是models.Account的信息
# 根據(jù)是否有pk, 來賦予form不同的場景, 根據(jù)不同的場景可以進(jìn)行不同的驗(yàn)證
if (obj is not None):
form.id = obj.pk
form.scene = 'update'
else:
form.scene = 'insert'
return form
get_form方法的參數(shù)request
保存的是HttpRequest操作對(duì)象
而obj是當(dāng)前操作的數(shù)據(jù)模型對(duì)象(Model), 而且在新增的操作時(shí), obj是None, 只有在編輯時(shí), 才存在obj
form = super(AccountAdmin, self).get_form(request, obj=obj, **kwargs)
會(huì)返回當(dāng)前操作的form對(duì)象
如果obj不是None, 則當(dāng)前操作是編輯, 我們就可以給form增加一個(gè)自定義的屬性scene(場景) = 'update', 否則就是新增
并且我們?cè)贔orm表單中, 可以針對(duì)不同的場景, 進(jìn)行不同的驗(yàn)證判斷
# account/forms.py
class AccountForm(forms.ModelForm):
def clean_account(self):
# 自動(dòng)驗(yàn)證account字段
if self.scene == 'insert':
_info = Account.objects.filter(
account=self.cleaned_data['account'],
is_deleted=0).values('id')
elif self.scene == 'update':
_info = Account.objects.filter(~Q(id=self.id) & Q(
account=self.cleaned_data['account']) & Q(
is_deleted=0)).values('id')
if _info:
raise ValidationError('用戶已存在')
return self.cleaned_data['account']
這個(gè)驗(yàn)證的意思是, 如果當(dāng)前場景(self.scene)是insert, 就只按照account查詢, 如果是update, 則增加id不為當(dāng)前操作id的過濾條件
django.contrib.auth.hashers 有兩個(gè)關(guān)于密碼加密的操作方法
make_password和check_password
make_password 是將指定的明文密碼加密
check_password 是校驗(yàn)給出的明文密碼是否正確
當(dāng)新增用戶時(shí), 密碼框必填, 當(dāng)編輯時(shí), 密碼框非必填。如果填了, 則修改密碼, 如果沒填, 則不更改密碼
第一步, 我們?cè)谛r?yàn)輸入的密碼時(shí), 同時(shí)也需要實(shí)現(xiàn)對(duì)密碼的加密(畢竟數(shù)據(jù)庫被人破解了, 后臺(tái)還是很嚴(yán)重的)
from django import forms
class AccountForm(forms.ModelForm):
def clean_password(self):
# 自動(dòng)驗(yàn)證密碼字段
if self.scene == 'insert':
if not self.cleaned_data['password']:
raise ValidationError('請(qǐng)輸入密碼')
elif self.scene == 'update':
if not self.cleaned_data['password']:
return None
else:
return self.cleaned_data['password']
return make_password(self.cleaned_data['password'])
這個(gè)也很好理解, 如果新增用戶時(shí), 未輸入密碼, 則返回錯(cuò)誤
更新時(shí)沒有輸入密碼, 則返回None
如果輸入, 就返回make_password加密后的密碼字符串
根據(jù)之前教程的scene場景參數(shù)的指定, 就可以跟容易的實(shí)現(xiàn)這個(gè)功能, 但是在編輯時(shí), 如果密碼為空不填的話, 密碼居然也會(huì)被設(shè)置為空。
這個(gè)是因?yàn)? 如果不填寫密碼, 的model對(duì)象會(huì)把password=None一直帶著, 轉(zhuǎn)換為sql執(zhí)行時(shí), 就變成password=''了
所以, 如果password沒有輸入值, 我們就要在執(zhí)行保存之前, 干掉model攜帶的password, 這樣才正確。
重寫ModelAdmin的保存方法
class AccountAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
if form.cleaned_data['password'] is None:
del obj.password
super().save_model(request, obj, form, change)
form.cleaned_data中就是表單提交后驗(yàn)證過的數(shù)據(jù), 如果password是None, 就del掉, 然后調(diào)用父類的save_model方法, 繼續(xù)執(zhí)行保存操作。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。