Django中的Form ModelForm
Django中的Form & ModelForm
在web開發中,處理表單是家常便飯,我們當然可以直接在模板中編寫原生的html表單,然后在后端進行表單驗證,這常常是一項繁瑣的工作。
Form
我們可以通過Django提供了表單功能,來方便表單的處理。下面看一下如何使用:
自定義表單類
from django import forms # 導入表單模塊 from django.core.exceptions import ValidationError class RegisterForm(forms.Form): # 自定義表單類,并繼承forms.Formemail = forms.EmailField(widget=forms.EmailInput( attrs={"class": "form-control"}))username = forms.CharField(min_length=4, max_length=12, widget=forms.TextInput(attrs={"class": "form-control"}))password = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={"class": "form-control"}))password2 = forms.CharField(min_length=6, widget=forms.PasswordInput(attrs={"class": "form-control"}))valid_code = forms.CharField(widget=forms.TextInput(attrs={"class": "form-control"}))def __init__(self, request, *args, **kwargs):# 如果需要額外接收參數,要重寫構造器函數# 這里額外接收一個參數,用于從request.sesssion中提取之前保存的驗證碼super(RegisterForm,self).__init__(*args, **kwargs) self.request = request# 自定義方法(局部鉤子),密碼必須包含字母和數字def clean_password(self):if self.cleaned_data.get('password').isdigit() or self.cleaned_data.get('password').isalpha():raise ValidationError('密碼必須包含數字和字母')else:return self.cleaned_data['password']def clean_valid_code(self): # 檢驗驗證碼正確;之前生成的驗證碼保存在了了session中if self.cleaned_data.get('valid_code').upper() == self.request.session.get('valid_code'):return self.cleaned_data['valid_code']else:raise ValidationError('驗證碼不正確')# 自定義方法(全局鉤子, 檢驗兩個字段),檢驗兩次密碼一致;def clean(self):if self.cleaned_data.get('password') != self.cleaned_data.get('password2'):raise ValidationError('密碼不一致')else:return self.cleaned_data# 注意,上面的字典取值用get, 因為假如在clean_password中判斷失敗,那么沒有返回值,最下面的clean方法直接取值就會失敗說明:
實例化表單對象,傳入模板渲染
# 實例化表單對象; 因為重寫了init方法,這里要額外接收requestform_obj = RegisterForm(request) <form action="{% url 'register' %}" method="post" id="register">{% csrf_token %}<div class="form-group"><label for="id_email">郵箱 </label><span class="error-info"></span>{{ form_obj.email }} <!-- span 標簽用于放置錯誤信息 --></div>......<p class="reg-button"><button type="submit" class="btn btn-primary btn-block">注冊</button></p></form>說明:
<form>{{form_obj.as_p}}</form>也可以通過as_p方法直接生成整個表單,但是自定義性比較差。
?
驗證提交的表單
# 傳入request.POST,實例化表單對象用于戶驗證和提取數據form_obj = RegisterForm(request, request.POST) # 方法:form_obj.is_valid() # 根據字段屬性和驗證函數進行校驗 form.cleaned_data.get(field) # 校驗OK,提取數據 form.errors # 錯誤信息:{field:[error_info], field:[],...}根據用戶提交,保存或修改數據庫
略……
ModelForm
用過Django Form功能的可能會覺得用起來很繁瑣,要寫一堆驗證方法,驗證通過后還是要一一提取數據,最終的操作都是數據庫模型中進行保存或修改操作。不難發現Form表單的字段和對應的數據庫Model幾乎是一樣的,于是Django提供了ModelForm,極大的簡化了表單的處理和驗證。我們看下如何使用。
自定義表單,繼承ModelForm
from django.forms import ModelForm # 導入ModelForm from django.forms import fields from .models import UserInfo, Role, Permission, Menu # 導入Modelclass UserInfoModelForm(ModelForm):username = fields.CharField(required=True)"""可以在這里單獨定義字段的具體類型和屬性,同Form如果定義的字段在model中存在,則會覆蓋;"""class Meta:model = UserInfofields = '__all__'labels = {'username': '用戶名','password': '密碼','nickname': '昵稱','email': '郵箱',}說明:
model: 對應的數據庫model
fields: 選擇字段列表;’all’是選擇所有字段
exclude: 排除字段列表
widgets: 插件字典
labels: 前端顯示字段名
error_messages: 自定義錯誤提示
localized_fields: 本地化,如:根據不同時區顯示數據
數據庫中2016-12-27 04:10:57 setting中的配置TIME_ZONE = 'Asia/Shanghai'USE_TZ = True 則顯示:2016-12-27 12:10:57?
實例化表單對象,傳入模板,同Form
model_form = UserInfoModelForm()驗證提交的表單
model_form = UserInfoModelForm(request.POST) if model_form.is_valid():model_form.save()is_valid()方法會根據model字段的類型以及自定義驗證方法來驗證提交的數據;如果is_valid()驗證通過,直接save(),就可以完成向數據庫中插入一條記錄。
如果是修改記錄,需要傳入記錄對象
# 實例化表單對象,傳入模板model_form = UserInfoModelForm(instance=user_obj)# 驗證提交model_form = UserInfoModelForm(request.POST, instance=user_obj) if model_form.is_valid():model_form.save()ChoiceField字段
用于外鍵字段
例1:將外鍵字段定義為ChoiceField字段,這里以Form表單為例來說明:
class UserInfoForm(Form):name = fields.CharField(required=True, error_messages={'required': '用戶名不能為空'})password = fields.CharField(required=True, error_messages={'required': '密碼不能為空'})email = fields.EmailField(required=True)# depart為外鍵字段,那么我們可以將其變為下拉選項(非外鍵字段也可以,只要給它提供[(m, n), (m, n)]這種結構的數據源就可以)depart = fields.ChoiceField(choices=models.Department.objects.values_list('id', 'title'))depart字段的模板渲染結果:
<select name="depart" id="id_depart"><option value="1">銷售</option><option value="2">公關</option><option value="3">技術</option><option value="4">后勤</option> </select>用于自定義字段
例2:這里以ModelForm為例來進行說明:
def func():return [(1, '北京'),(2, '上海'),(3, '武漢'),]class UserInfoModelForm(ModelForm):city = fields.ChoiceField(choices=func())"""可以在這里單獨定義字段的具體類型和屬性,同Form如果定義的字段在model中存在,則會覆蓋;假設,model中的city字段是CharField類型,默認用ModelForm會生成input框,但是我們又不希望每次都手動填入數據。那么,就可以單獨定義該字段為ChoiceField類型。只需要為其提供[(m, n), (m, n)]這種結構的數據源就可以,比如這里選擇通過一個函數來實現。"""class Meta:model = UserInfofields = '__all__'labels = {'username': '用戶名','password': '密碼','email': '郵箱',}city字段的模板渲染結果:
<select name="city" id="id_city"><option value="1">北京</option><option value="2">上海</option><option value="3">武漢</option> </select>用于靜態字段
以上兩個栗子,分別用了外鍵的關聯表和函數返回值作為表單中ChoiceField字段的下拉選項數據。在實際使用中,常常在model中定義一個靜態字段,來作為數據來源,如下:
from django.db import modelsclass Department(models.Model):passclass UserInfo(models.Model):city_choices = [(1, '北京'),(2, '上海'),(3, '武漢'),]username = models.CharField(verbose_name='用戶名', max_length=32, null=True, blank=True)password = models.CharField(verbose_name='密碼', max_length=64, null=True, blank=True)email = models.EmailField(verbose_name='郵箱', max_length=64, null=True, blank=True)# city以靜態字段city_choices作為數據來源city = models.IntegerField(verbose_name='城市', choices=city_choices, null=True, blank=True)depart = models.ForeignKey(verbose_name='部門', to="Department", null=True, blank=True)bug
不論數據是來自靜態字段還是外鍵關聯表,ChoiceField字段的值都是是從數據庫獲取的。但是一旦數據源更新了,模板在渲染時無法顯示最新的數據;除非重啟程序。這是因為,定義了Form或ModelForm后,ChoiceField會從數據庫取一次值,后面再使用時這個值就不更新了。因此,要動態生成ChoiceField字段的數據。
解決辦法:不在定義階段獲取數據,在每次實例化表單時獲取數據,重寫__init__方法:
class UserInfoForm(Form):name = fields.CharField(required=True, error_messages={'required': '用戶名不能為空'})password = fields.CharField(required=True, error_messages={'required': '密碼不能為空'})email = fields.EmailField(required=True)# depart這里只定義字段類型depart = fields.ChoiceField()def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)# 實例化時才動態獲取數據self.fields['depart'].choices = models.Department.objects.values_list('id', 'title')除了上面這種手動的方式,我們也可以利用ModelChoiceField字段,達到動態獲取數據的目的:
class UserInfoForm(Form):name = fields.CharField(required=True, error_messages={'required': '用戶名不能為空'})password = fields.CharField(required=True, error_messages={'required': '密碼不能為空'})email = fields.EmailField(required=True)# ModelChoiceField字段動態獲取數據depart = ModelChoiceField(queryset=models.Department.objects.all())depart模板渲染結果:
<select name="depart" required="" id="id_depart"><option value="" selected="">---------</option><option value="1">銷售</option><option value="2">公關</option><option value="3">技術</option><option value="4">后勤</option> </select>不過這里還是推薦通過第一種,重寫__init__的方式,定制性更強;
第二種ModelChoiceField自動獲取數據,它接收QuerySet對象,因此,如果沒有在model中定義__str__方法,渲染出來的結果將是這樣子的。。。
<select name="depart" required="" id="id_depart"><option value="" selected="">---------</option><option value="1">Department object</option><!-- 沒有可讀性。。。 --> </select>總結
以上是生活随笔為你收集整理的Django中的Form ModelForm的全部內容,希望文章能夠幫你解決所遇到的問題。