1. 程式人生 > >如何正確使用 Django Forms

如何正確使用 Django Forms

Django forms使用容易, 又方便擴充套件, 因此Django admin和CBVs基本都基於forms使用. 事實上, 由於django forms的強大驗證功能, 大多數Django API 框架都是用forms作為其驗證的一部分.

雖然django forms的學習需要花費一點時間, 但如果將forms, models和views結合起來使用, 我們可以花費很少的經歷來完成龐大的工作.

  1. Django Forms的強大之處

有些django專案並不直接呈現HTML, 二是以API框架的形式存在, 但你可能沒有想到, 在這些API形式的django專案中也用到了django forms. django forms不僅僅是用來呈現HTML的, 他們最強的地方應該是他們的驗證能力. 下面我們就介紹幾種和Django forms結合使用的模式:

  1. 模式一: 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_&()的最後必須返回驗證完畢或修改後的值.

  1. 模式四, 自定義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類, 類可以繼承和被繼承, 也可以動態修改.