如何正确使用 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类, 类可以继承和被继承, 也可以动态修改.

原文链接: http://www.weiguda.com/blog/12/

你可能感兴趣的:(Django)