Django 1.6 最佳实践: 如何正确使用 Django Forms

在我们的博客中, 记录了我们在开发过程中所使用的技术和遇到的问题, 希望作为其他开发和设计者的一个学习交流平台.

Django 1.6 最佳实践: 如何正确使用 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结合使用的模式:

2. 模式一: 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_<field>()方法

如果我们希望验证form中的多个field, 或者验证涉及到已经存在之后的数据, 那么我们就需要用到form的clean()和clean_<field>&()方法了. 以下代码检查密码长度是否大于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_<field>&()的最后必须返回验证完毕或修改后的值.

5. 模式四, 自定义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://weiguda.com/blog/12/