Django第3章: Form表单

Posted fqh202

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django第3章: Form表单相关的知识,希望对你有一定的参考价值。

1. 常规表单
2. django中的form表单

form表单使用介绍

  1. 页面与web服务器交互数据最重要的方式;

  2. input标签类型: text, password, file;

  3. 默认方式method=get,所有信息都会在url后面显示,表单提交指定为method=post;

  4. 注意,若需要将数据发送到后端,input标签必须指定name属性,否则不会发送;

  5. 传输的数据以键值对形式保存在request.POST中, 键的值即为name属性值;

  6. 文件上传: 由于上传文件时在客户端与服务端传输的是二进制数据,与字符串数据不一样。传输二进制数据,不管是在form表单,还是在Ajax中,都有自己的传输方式。
    在form表单中,上传文件时要使用分片传输的方式enctype="multipart/form-data"
    • 若没有指定form的传输方式: file数据保存在request.POST中, 一般是文件名的str形式;

    • 若指定了文件传输类型: enctype="multipart/form-data",表单提交后文件以InMemoryUploadedFile的类对象单独保存在request.FILES中,

  7. 保存文件: 后台接收的文件对象无法直接通过open方法读取, 直接遍历文件对象即可;

    // 接受文件对象并保存至服务器
    file_obj = request.FILES.get('upload_file')
    f1 = open(file_obj.name, "wb")
    # 方法1
    for i in file_obj:
        f1.write(i)
    # 方法2
    for i in file_obj.chunks():
        f1.write(i)


Django的form表单介绍

form表单的升级版, 可以给表单元素扩展许多功能:

  1. 在模板生成html的input标签;
  2. 验证用户数据并显示错误信息;
  3. 保留上次提交数据;
  4. 初始化页面显示内容;

form字段及参数

required=True,               是否允许为空
widget=None,                 HTML插件
disabled=False,              是否可以编辑
initial=None,                初始值
label=None,                  用于生成Label标签或显示内容
help_text='',                帮助信息(在标签旁边显示)
error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}, 此处和model字段定义错误信息类似, 也有max_length, min_length等错误信息code

show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[],               自定义验证规则
localize=False,              是否支持本地化
label_suffix=None            Label内容后缀

CharField
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
    
IntegerField
    max_value=None,              最大值
    min_value=None,              最小值

FloatField
    ...
 
DecimalField
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度

RegexField
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略错误信息,错误信息使用   error_messages={'invalid': '...'}
    

EmailField      
    ...

# 重要   
FileField
    allow_empty_file=False     是否允许空文件
 
ImageField    
    需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        1. form表单中 enctype="multipart/form-data"
        2. view函数中 obj = MyForm(request.POST, request.FILES)
 
URLField
    ...
 
 
BooleanField
    ...

ChoiceField
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示

FilePathField       文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''

常用控件

// radio单选按钮,值为字符串==========================
gender = forms.ChoiceField(
    choices=((1, '男性'),(2, '女性'), (3, '中性'), ), initial=1, 
    widget=widgets.Radioselect
)

gender = forms.CharField(
    initial=1, widget=widgets.RadioSelect(choices=((1, '男性'),(2, '女性'), (3, '中性'), ))
)


// 单select,值为字符串========================================
user = fields.CharField(
     initial=2,
     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
)

user = fields.ChoiceField(
     choices=((1, '上海'), (2, '北京'),),
     initial=2,
     widget=widgets.Select
)
 

// 多选select,值为列表==========================================
user = fields.MultipleChoiceField(
     choices=((1,'上海'),(2,'北京'),),
     initial=[1,],
     widget=widgets.SelectMultiple
)
 

// 单checkbox
gender = forms.ChoiceField(initial=[2, ],choices=((1, '上海'), (2, '北京'),), widget=widgets.CheckboxInput)


// 多选checkbox,值为列表
user = fields.MultipleChoiceField(
     initial=[2, ],
     choices=((1, '上海'), (2, '北京'),),
     widget=widgets.CheckboxSelectMultiple
)
关于使用select等控件时的取值问题
  1. 先将内容以元祖嵌套格式取出, Obj.objects.all().values_list(‘id‘, ‘name‘);
  2. 作为参数传入;
class UserForm(forms.Form):
    choices = City.objects.all().values_list('id', 'name')
    hometown = forms.ChoiceField(initial=2, widget=widgets.Select(choices=choices))

Django的form使用步骤

  1. 创建后台表单验证类,继承form.Form;

    from django import forms
    from django.forms import widgets
    
    class UserForm(forms.Form):
        username = forms.CharField(min_length=2, max_length=12, label='用户名', error_messages={'required': '用户名不能为空', 'min_length': '必须大于2位'}, widget=widgets.TextInput(attrs={"placeholder": "用户名", "class": "form-control s", 'label':'username', }))
    # 可以在后端定制input样式,直接给即将生成的input标签增加属性
        password = forms.CharField(label='密码',min_length=4,widget=widgets.PasswordInput(attrs={"placeholder": "密码","class": "form-control"}))
    
        email = forms.EmailField(label='邮箱', widget=widgets.PasswordInput(attrs={"placeholder": "密码","class": "form-control"}))
    
        gender = forms.ChoiceField(choices=((1, 'male'),(2, 'female')), initial=1, widget=widgets.RadioSelect)
    
        hometown = forms.CharField(
            initial=2,
            widget=widgets.Select(choices=((1, '武汉'), (2, '广州'),))
    )
    
  2. 页面根据类的对象自动创建html标签, 注意, 在form标签中加上novalidate可以关闭浏览器的自动验证功能, 方便前期调试;

    <link rel="stylesheet" href="{% static 'css/bootstrap.css' %}">
    
    <div class="container form">
    <div class="row">
         <div class="col-md-8">
            <form action="" method="post" enctype="multipart/form-data" novalidate>
                <div class="form-group">
                    <p>username:{{ form.username }}<span>{{ error_msg.username.0 }}</span></p>
                </div>
                <div class="form-group">
                    <p>password:{{ form.password }}<span>{{ error_msg.password.0 }}</span></p>
                </div>
                <div class="form-group">
                    <p>email:{{ form.email }}<span>{{ error_msg.email.0 }}</span></p>
                </div>
                <div class="form-group">
                    <p>gender:{{ form.gender }}<span>{{ error_msg.gender.0 }}</span></p>
                </div>
                <div class="form-group">
                    <p>hometown:{{ form.hometown }}<span>{{ error_msg.gender.0 }}</span></p>
                </div>
                <div class="form-group"><input type="submit"></div>
                {% csrf_token %}
            </form>
         </div>
    
        </div>
    
    </div>
    
  3. 视图函数接收表单数据, 判断并返回信息

    def register(request):
    error_msg = ''
    if request.method == 'GET':
        # 1. 实例化空form对象, 并传到模板,生成空的input标签
        my_form = UserForm()
        return render(request, 'register.html', {"form": my_form, 'error_msg': error_msg})
    
    if request.method == "POST":
        # 1. 创建form对象,随后利用前端form提交的信息对齐进行填充
        form_post = UserForm(request.POST)
    
        # 2. 检查提交的表单语法是否正确
        if form_post.is_valid():
            print(form_post.clean())
            # 默认检测所有的输入框不能为空
            # 特有的取值方法:通过cleaned_data属性取到所有的输入值
            username = form_post.cleaned_data.get('username', '')
            password = form_post.cleaned_data.get('password', '')
            email = form_post.cleaned_data.get('email', '')
    
        else:
            # 检查错误信息
            # 可以用ie浏览器检测,chrome和火狐都会默认在前端显示错误,不会提交
            error_msg = form_post.errors
            print(error_msg)  # <class 'django.forms.utils.ErrorDict'>
            return HttpResponse('信息有误')

内置错误信息提示

在前面三个步骤的基础上, 对于错误在页面的提示作以下说明,见代码;
注意:

# 简单的错误信息定制,
error_messages={
    'required': '用户名不能为空', 'min_length': '必须大于*位',
    'invalid' : '****', 'max_length': '不能超过*位',...
},


# is.valid()没通过的分支
error_msg = form_post.errors
print(error_msg)  # <class 'django.forms.utils.ErrorDict'>
return render(request, 'register.html', {
    "error_msg": error_msg,
    'form': form_post,
})


# html页面
<span class="errors">{{ error_msg.username.0 }}</span>
# 有对应的字段错误,就取值并显示

自定制错误信息(重点)

正则匹配的自定义验证
  1. 存在多条验证时, 以列表或者元祖形式导入实例化的RegexValidator对象, django会按照顺序逐个验证,直到抛出错误信息;
  2. 传入两个参数: 匹配的正则表达式错误信息;
  3. 原理是若输入与正则不匹配,则抛出ValidationError(错误信息)
from django.form import Form
from django.core.validators import RegexValidator

class UserForm(Form):
    username = forms.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
        widget=widgets.TextInput(attrs={'class': 'form-control'}))
自定义验证函数
from django.core.exceptions import ValidationError
import re

def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手机号码格式错误')


class UserForm(forms.Form):
    username = forms.CharField(validators=[mobile_validate, ], widget=widgets.TextInput(attrs={'class': 'form-control'}),)
重载内置的clean_field方法
  1. 在form字段中定义的验证完成后,会执行clean_field()方法;
  2. 此时通过cleaned_data取出要进一步判断的值;
  3. 自定义验证函数必须有返回值, 即合格的值, 不合格直接抛出自定义的错误信息即可;
from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError

class UserForm(Form):
    username = forms.CharField(widget=widgets.TextInput(attrs={'class': 'form-control'}),)
    
    def clean_username(self):
        value = self.cleaned_data['username']
        if value == 'alex':
            raise ValidationError('用户名已存在')
        return value
全局自定制验证clean方法
  1. 实现前提条件: 因为clean(self)是在clean_field(self)之后运行的;
  2. 若验证没用过, 自定制的错误信息会存储在django.forms.utils.ErrorDict中, __all__:[‘错误信息‘, ];
  3. 在前端不能直接通过errors_dict.key.0取出,需要自定义过滤器,取出__all__的值;
# 例如:验证用户名和邮箱是否一致

// 后台自定制多字段验证方法==============
def clean(self):
    if self.cleaned_data.get('username') != self.cleaned_data.get('email'):
        raise ValidationError('用户名和邮箱不一致')
    else:
        return self.cleaned_data

// 自定义过滤器==========================
from django import template
register = template.Library()

@register.filter
def get_error(error_dict):
    return error_dict.get('__all__')


// 前端取值=============================
 <div id="error_msg">
     {% if error_msg %}
        {{ error_msg|get_error }}
     {% endif %}
 </div>

以上是关于Django第3章: Form表单的主要内容,如果未能解决你的问题,请参考以下文章

笔记《深入理解bootstrap》第3章(下) —— CSS布局

第5章 jQuery对表单表格的操作及更多应用

第 10 章 表单元素[上]

# Django adminform表单的应用记录

Django高级-Form表单组件应用

第4章 表单和图片