如何正確使用 Django Forms
Django forms使用容易, 又方便擴充套件, 因此Django admin和CBVs基本都基於forms使用. 事實上, 由於django forms的強大驗證功能, 大多數Django API 框架都是用forms作為其驗證的一部分.
雖然django forms的學習需要花費一點時間, 但如果將forms, models和views結合起來使用, 我們可以花費很少的經歷來完成龐大的工作.
- Django Forms的強大之處
有些django專案並不直接呈現HTML, 二是以API框架的形式存在, 但你可能沒有想到, 在這些API形式的django專案中也用到了django forms. django forms不僅僅是用來呈現HTML的, 他們最強的地方應該是他們的驗證能力. 下面我們就介紹幾種和Django forms結合使用的模式:
- 模式一: ModelForm和預設驗證
最簡單的使用模式便是ModelForm和model中定義的預設驗證方式的組合:
# myapp/views.py from django.views.generic import CreateView, UpdateView from braces.views import LoginRequiredMixin from .models import Article class ArticleCreateView(LoginRequiredMixin, CreateView): model = Article fields = ('title', 'slug', 'review_num') class ArticleUpdateView(LoginRequiredMixin, UpdateView): model = Article fields = ('title', 'slug', 'review_num')
正如以上程式碼中看到的一樣:
ArticleCreateView和ArticleUpdateView中設定model為Article
兩個view都基於Article model自動生成了ModelForm
這些ModelForm的驗證, 是基於Article model中定義的field轉換而來的
3. 模式二, 在ModelForm中修改驗證
在上面的例子中, 如果我們希望每篇article title的開頭都是”new”, 那麼應該怎麼做呢? 首先我們需要建立自定義的驗證(validator):
# utils/validator.py from django.core.exceptions import ValidationError def validate_begins(value): if not value.startswith(u'new'): raise ValidationError(u'Must start with new')
可見, 在django中的驗證程式就是不符合條件便丟擲ValidationError的function, 為了方便重複使用, 我們將它們放在django app utils的validators.py中.
接下來, 我們可以在model中加入這些validator, 但為了今後的方便修改和維護, 我們更傾向於加入到ModelForm中:
# myapp/forms.py
from django import forms
from utils.validators import validate_begin
from .models import Article
class ArticleForm(forms.ModelForm):
dev __init__(self, *args, **kwargs):
super(ArticleForm, self).__init__(8args, **kwargs)
self.fields["title"].validators.append(validate_begin)
class Meta:
model = Article
Django的edit views(UpdateView和CreateView等)的預設行為是根據view中model屬性, 自動建立ModelForm. 因此, 我們需要呼叫我們自己的Modelform來覆蓋自動建立的:
# myapp/views.py
from django.views.generic import CreateView, UpdateView
from braces.views import LoginRequiredMixin
from .models import Article
from .forms import ArticleForm
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
fields = ('title', 'slug', 'review_num')
form_class = ArticleForm
class ArticleUpdateView(LoginRequiredMixin, UpdateView):
model = Article
fields = ('title', 'slug', 'review_num')
form_class = ArticleForm
4. 模式三, 使用form的clean()和clean_()方法
如果我們希望驗證form中的多個field, 或者驗證涉及到已經存在之後的資料, 那麼我們就需要用到form的clean()和clean_&()方法了. 以下程式碼檢查密碼長度是否大於7位, 並且password是否和password2相同:
# myapp/forms.py
from django import forms
class MyUserForm(forms.Form):
username = forms.CharField()
password = forms.CharField()
password2 = forms.CharField()
def clean_password(self):
password = self.cleaned_data['password']
if len(password) <= 7:
raise forms.ValidationError("password insecure")
return password
def clean():
cleaned_data = super(MyUserForm, self).clean()
password = cleaned_data.get('password', '')
password2 = cleaned_data.get('password2', '')
if password != password2:
raise forms.ValidationError("passwords not match")
return cleaned_data
其中需要注意的是, clean()和clean_&()的最後必須返回驗證完畢或修改後的值.
- 模式四, 自定義ModelForm中的field
我們會經常遇到在form中需要修改預設的驗證, 比如一個model中有許多非必填項, 但為了資訊完整, 你希望這些field在填寫時是必填的:
# myapp/models.py
from django.db import models
class MyUser(models.Model):
username = models.CharField(max_length=100)
password = models.CharField(max_length=100)
address = models.TextField(blank=True)
phone = models.CharField(max_length=100, blank=True)
為了達到以上要求, 你可能會通過直接增加field改寫ModelForm:
# 請不要這麼做
# myapp/forms.py
from django import forms
from .models import MyUser
class MyUserForm(forms.ModelForm):
# 請不要這麼做
address = forms.CharField(required=True)
# 請不要這麼做
phone = forms.CharField(required=True)
class Meta:
model = MyUser
請不要這麼做, 因為這違反”不重複”的原則, 而且經過多次的拷貝貼上, 程式碼會變得複雜難維護. 正確的方式應當是利用init():
# myapp/forms.py
from django import forms
from .models import MyUser
class MyUserForm(forms.ModelForm):
def __init__(self, *args, **kwarg):
super(MyUserForm, self).__init__(*args, **kwargs)
self.fields['address'].required = True
self.fields['phone'].required = True
class Meta:
model = MyUser
值得注意的是, Django forms也是Python類, 類可以繼承和被繼承, 也可以動態修改.