最近在学习Django的过程中,发现Django里面对表单的支持非常棒,简化了很多操作,例如可以很方便的生成Html表单,对表单进行验证以及最终的数据存储等等,简单的一个实例如下:

class AccountForm(forms.ModelForm):
    nick_name = forms.CharField(label=u'用户昵称', \
                                help_text="请输入用户昵称")
    email = forms.EmailField(label=u'电子邮件', \
                                help_text=u'请输入常用邮箱')
    password = forms.CharField(label=u'密码', \
                                help_text=u'请输入密码', \
                                widget=forms.PasswordInput())
    re_password = forms.CharField(label=u'确认密码', \
                                help_text=u'请再次确认密码', \
                                widget=forms.PasswordInput())
    

    def clean_re_password(self):
        password = self.cleaned_data.get("password")
        re_password = self.cleaned_data.get("re_password")
        if password and re_password and password != re_password:
            raise forms.ValidationError((u'密码输入不一致'))
        if len(password) < 6:
            raise forms.ValidationError((u'密码至少6位'))
        return re_password

    class Meta:
        model = Account
        fields = ('nick_name', 'email', 'password')

在模板中只需要简单的添加

<div class="weui_cells_form">
    <form id="user_form" method="post" action="url_to_submit_form"
        <!-- csrf_token fields is necessary for securty -->
        {% csrf_token %}
        {{account_form}}
        <input type="submit" name="submit" value="注册" />
    </form>
</div>

这样就完成了简单的表单设计。

Django的这种方法确实很简单,但是Django固有的表单样式比较单一,虽然可以通过widget属性添加css样式,但是明显DIY程度不够高,本着前后端分离的原则,需要将表单的样式彻底分离出来,做到完全定制。

具体的表单自定义方式实际在Django的官方文档里面也有提及

Useful attributes on {{ field }} include:
{{ field.label }}
The label of the field, e.g. Email address.
{{ field.label_tag }}
The field’s label wrapped in the appropriate HTML <label> tag. This includes the form’s label_suffix. For example, the default label_suffix is a colon:
<label for="id_email">Email address:</label>
{{ field.id_for_label }}
The ID that will be used for this field (id_email in the example above). If you are constructing the label manually, you may want to use this in lieu of label_tag. It’s also useful, for example, if you have some inline JavaScript and want to avoid hardcoding the field’s ID.
{{ field.value }}
The value of the field. e.g someone@example.com.
{{ field.html_name }}
The name of the field that will be used in the input element’s name field. This takes the form prefix into account, if it has been set.
{{ field.help_text }}
Any help text that has been associated with the field.
{{ field.errors }}
Outputs a <ul class="errorlist"> containing any validation errors corresponding to this field. You can customize the presentation of the errors with a {% for error in field.errors %} loop. In this case, each object in the loop is a simple string containing the error message.
{{ field.is_hidden }}

通过这种方式,就可以实现对Form的完全定制,比如采用微信的样式:

<div class="weui_cells weui_cells_form">
    {% for item in login_form %}
        <div class="weui_cell">
            <div class="weui_cell_hd">
                <label class="weui_label">{{item.label}}</label>
            </div>
            <div class="weui_cell_bd weui_cell_primary">
                <input class="weui_input" placeholder="{{item.help_text}}" id="{{item.id_for_label}}" name="{{item.html_name}}" type="{{item.field.widget.input_type}}" />
            </div>
            {% for error in item.errors %}
                    <p>{{error}}</p>
                {% endfor%}
        </div>
</div>

在上面的"item.field.widget.input_type"这个域在文档中也并未提及,但是这个域又相当重要,因为像输入密码日期之类的类型都是靠这个域来定义的,最后这个域是通过读Django的源代码得到的,也算是一种获得Doc的途径吧。