美文网首页
(8)Django - 表单

(8)Django - 表单

作者: libdream | 来源:发表于2019-03-10 14:50 被阅读0次

表单是搜集用户数据信息,实现网页数据交互的关键。Django的表单功能由Form类实现,主要分两种:django.forms.Formdjango.forms.ModelForm。前者是一个基础的表单功能,后者是在前者的基础上结合模型所生成的数据表单。
虽然在模板文件中直接编写表单是一种较为简单的实现方法,但如果表单元素较多,会在无形之中增加模板的代码量,对日后维护和更新造成极大的不便。为了简化表单的实现过程和提高表单的灵活性,Django提供了完善的表单功能。先来看个简单的例子。
在mysite的index中添加一个form.py用于编写表单的实现功能,然后在templates文件夹中添加模板data_form.html,用于将表单数据显示到网页上。
首先在form.py中添加以下代码:

#index 的 form.py
from django import forms
from .models import *

class ProductForm(forms.Form):
    name = forms.CharField(max_length=20, label='名字')
    weight = forms.CharField(max_length=50, label='重量')
    size = forms.CharField(max_length=50, label='尺寸')
    #设置下拉框的值
##    choices_list = [(1,'手机'),(2, '平板')]
    choices_list = [(i+1, v['type_name']) for i,v in enumerate(Type.objects.values('type_name'))]
    type = forms.ChoiceField(choices=choices_list, label='产品类型')

在form.py中定义表单类ProductForm,类属性就是表单字段,对应HTML里的每一个控件。
然后在视图函数中导入form.py所定义的ProductForm类,在index函数中实例化生成对象,再将对象传递给模板文件。

#index 的 views.py
from django.shortcuts import render
from .form import *

def index(request):
    product = ProductForm()
    return render(request, 'data_form.html', locals())

最后在模板文件data_form.html中将对象product以HTML的<table>的形式展现在网页上。

templates 下的 data_form.html
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="" method="post">
    <table>
        {{ product.as_table }}
    </table>
    <input type="submit" value="提交">
</form>
</body>
</html>

保存后运行结果如下:


image.png

表单的定义

Django的表单功能主要是通过定义表单类,再由类的实例化生成HTML的表单元素控件,以减少再模板中的硬编码。上例中的<table>源代码如下:

<table>
<tr><th><label for="id_name">名字:</label></th><td><input type="text" name="name" maxlength="20" required id="id_name"></td></tr>
<tr><th><label for="id_weight">重量:</label></th><td><input type="text" name="weight" maxlength="50" required id="id_weight"></td></tr>
<tr><th><label for="id_size">尺寸:</label></th><td><input type="text" name="size" maxlength="50" required id="id_size"></td></tr>
<tr><th><label for="id_type">产品类型:</label></th><td>
<select name="type" id="id_type">
  <option value="1">手机</option>
  <option value="2">平板电脑</option>
  <option value="3">智能穿戴</option>
  <option value="4">通用配件</option>
</select></td></tr>
</table>

通过对比,可以很容易发现HTML源代码和定义的ProductForm类中的字段和参数的对应关系。
除了上述表单字段外,Django还提供多种内置的表单字段:

字段 说明
BooleanField 复选框,如果字段带有initial=True, 复选框被勾上
CharField 文本框,参数max_length和min_length
ChoiceField 下拉框,参数choices设置数据内容
TypedChoiceField 与ChoiceField相似,但多出参数coerce和empty_value,分别代表强制转换数据类型和用于表示空值,默认为空字符串
DateField 文本框,具有验证日期格式的功能,参数input_formats设置日期格式
EmailField 文本框,验证输入数据是否符合邮箱格式,可选参数max_length和min_length
FileField 文件上传功能,参数max_length和allow_empty_file分别设置最大长度和文件内容是否为空
FilePathField 在特定的目录选择并上传文件,参数path是必需参数,参数recursive、match、allow_files和allow_folders为可选参数
FloatField 验证数据是否为浮点数
ImageField 验证文件是否为Pillow库可识别的图像格式
IntegerField 验证数据是否为整型
GenericIPAddressField 验证数据是否为有效数值
SlugField 验证数据是否只包含字母、数字、下划线及连字符
TimeField 验证数据是否为datetime.time或指定特定时间格式的字符串
URLField 验证数据是否为有效的URL地址

表单字段除了转换HTML控件之外,还具有一定的数据格式规范,规范主要由字段类型和字段共同实现。每个不同类型的表单字段都由一些自己特殊的参数,但每个表单字段都具有以下的共同参数:

参数 说明
Required 输入数据是否为空,默认为True
Widget 设置HTML控件的样式
Label 生成Label标签或显示内容
Initial 设置初始值
help_text 设置帮助提示信息
error_messages 设置错误信息,以字典格式表示:{‘required’:'不能为空','invalid':'格式错误'}
show_hidden_initial 值为True或False,是否在当前控件后面再加一个 隐藏的且具有默认值的控件(可用于检验两次输入值是否一致)
Validators 自定义数据验证规则。以列表格式表示,列表元素为函数名
Lacalize 值为True/False,是否支持本地化,如不同时区显示相应的时间
Disabled 值为True/False,是否可以编辑
label_suffix Label内容后缀,在Label后添加内容

根据以上参数说明,对form.py的表单ProductForm的字段进行优化:

#form.py
from django import forms
from .models import *
from django.core.exceptions import ValidationError

#自定义数据验证函数
def weight_validate(value):
    if not str(value).isdigit():
        raise ValidationError('请输入正确的重量 ')
    

class ProductForm(forms.Form):
    #设置错误信息并设置样式
    name = forms.CharField(max_length=20, label='名字',
                           widget=forms.widgets.TextInput(attrs={'class':'c2'}),
                           error_messages={'required':'名字不能为空'})
    #使用自定义数据验证函数
    weight = forms.CharField(max_length=50, label='重量',
                             validators=[weight_validate])
    size = forms.CharField(max_length=50, label='尺寸')
    #设置下拉框的值
##    choices_list = [(1,'手机'),(2, '平板')]
    choices_list = [(i+1, v['type_name']) for i,v in enumerate(Type.objects.values('type_name'))]
    #设置CSS样式
    type = forms.ChoiceField(widget=forms.widgets.Select(attrs={'class':'type','size':3}),
                             choices=choices_list, label='产品类型')

优化的代码分别使用了参数widget、label、error_messages和validators,这4个参数是实际开发中常用的参数。为了进一步验证优化后的表单是否正确运行,还要对views.py的视图函数index代码进行优化

#views.py 的 index 函数
from django.http import HttpResponse
from django.shortcuts import render
from .form import *

def index(request):
    #GET请求
    if request.method == 'GET':
        product = ProductForm()
        return render(request, 'data_form.html', locals())
    #POST请求
    else:
        product = ProductForm(request.POST)
        if product.is_valid():
            #获取网页控件name的数据
            #方法1
##            name = product['name']
            #方法2
            #cleaned_data将控件name的数据进行清洗,转换成Python数据类型
            name = product.cleaned_data['name']
            return HttpResponse('提交成功')
        else:
            #将错误信息输出,error_msg是将错误信息以json格式输出
            error_msg = product.errors.as_json()
            return render(request, 'data_form.html', locals())

上述代码,首先判断了用户的请求方式,index函数对GET和POST请求做了不同的响应处理。

  • 用户首次访问url地址时,等于向项目发送一个GET请求,函数index将表单ProductForm实例化并传递给模板,由模板引擎生成HTML表单返回给用户。
  • 当用户在表单中输入相关信息并提交时,等于向项目发送一个POST请求,函数index首先获取表单数据对象product,然后由is_valid()方法对数据对象product进行数据验证。
  • 如果验证成功,可以使用product['name']或prodcut.cleaned_data['name']方法来获取用户在某个控件上的输入值,实现表单和模型的信息交互。
    下面来验证一下是否能正常工作,在姓名栏输入了空格


    image.png

    点击提交按钮后,


    image.png
    验证正常工作了。
    在上述例子中,模板data_form.html的表单是使用HTML的<table>标签展现在网页上,除此之外,表单还可以用其他HTML标签展现,只需将模板data_form.html的对象product使用以下方法即可生成其他HTML标签:

将表单生成HTML的 ul 标签
{{ product.as_ul }}
将表单生成HTML的 p 标签
{{ product.as_p }}
生成单个HTML元素控件
{{ product.type }}
获取表单字段的参数 label 属性值
{{ product.type.label }}

模型与表单

Django的表单分两种:基础表单django.forms.Form和数据表单django.forms.ModelForm。数据表单是将模型的字段转换成表单的字段,再从表单的字段生成HTML的元素控件。Django通过ModelForm表单功能模块实现了表单数据与模型数据之间的交互开发。
首先再form.py中定义表单ProductModelForm。该类可分为三大部分:添加模型外的表单字段、模型与表单设置和自定义表单字段的数据清洗。

from django import forms
from .models import *
from django.core.exceptions import ValidationError

#数据库表单
class ProductModelForm(forms.ModelForm):
    #步骤1:添加模型外的表单字段
    productId = forms.CharField(max_length=20, label='产品序号')

    #步骤2:模型与表单设置
    class Meta:
        #绑定模型,必选
        model = Product
        #设置转换字段,必选,属性值为'__all__'时全部转换
        #fields = '__all__'
        fields = ['name','weight','size','type']
        #禁止模型转换的字段,可选,若设置了该属性,fields则可以不设置
        exclude = []
        #设置HTML元素控件的label标签,可选
        labels = {'name':'产品名称',
                  'weight':'重量',
                  'size':'尺寸',
                  'type':'产品类型',
            }
        #设置表单字段的CSS样式,可选
        widgets = {'name':forms.widgets.TextInput(attrs={'class':'c1'}),
            }
        #定义字段的类型,可选,默认时自动转换的
        field_classes = {
            'name': forms.CharField,
            }
        #设置提示信息
        help_texts = {
            'name':'',
            }
        #自定义错误信息
        error_messages = {
            #设置全部错误信息
            '__all__':{'required':'请输入内容',
                       'invalid':'请检查输入内容'},
            #设置某个字段的错误信息
            'weight':{'required':'请输入重量数值',
                      'invalid':'请检查数值是否正确'},
            }

    #步骤3: 自定义表单字段的数据清洗
    def clean_weight(self):
        #获取字段weight的值
        data = self.cleaned_data['weight']
        return data + 'g'

模型字段转换成表单字段主要在类Meta中实现,由类Meta的属性实现两者之间的转换,其属性说明如下:

属性 说明
model 必需属性,用于绑定Model对象
fields 必需属性,设置模型内哪些字段转换成表单字段。属性值为"all"时表示整个模型的字段,若设置一个或多个,使用列表或元组表示,列表或元组里的元素是模型的字段名
exclude 可选属性,与fields相反,表示禁止模型哪些字段转换成表单字段。若设置了该属性,则属性fields可以不用设置
labels 可选属性,设置表单字段里的参数label。属性值以字典表示,字典的键是模型的字段
widgets 可选属性,设置表单字段里的参数widget
field_classes 可选,将模型的字段类型重新定义为表单字段类型,默认模型字段类型会自动转换为表单字段类型
help_texts 可选,设置表单字段里的参数help_text
error_messages 可选,设置表单字段里的参数error_messages

需要注意的是,一些较为特殊的模型字段在转换表单时会有不同的处理方式。例如模型字段的类型为AutoField,该字段在表单中不存在对应的表单字段;模型字段类型为ForeignKey和ManyToManyField,在表单中对应的表单字段为ModelChoiceField和ModelMultipleChoiceField。
在自定义数据清洗函数时,必须以“clean_字段名”的格式作为函数名,而且函数必须有return返回值。如果在函数中设置了ValidationError异常抛出,那么该函数可视为带有数据验证的清洗函数。
通过定义表单类ProductModelForm将模型Product与表单相互结合起来后,还要通过视图函数来使用和展现表单,继续沿用前面的模板data_form.html,在项目的urls.py和views.py中分别定义新的URL地址和视图函数:

#urls.py 
path('<int:id>.html', views.model_index),

#views.py 的视图函数 model_index
def model_index(request, id):
    if request.method == 'GET':
        instance = Product.objects.filter(id=id)
        #判断数据是否存在
        if instance:#如果存在,将数据传递给参数instance
            product = ProductModelForm(instance=instance[0])
        else:#如果不存在,为name字段设置一个默认值
            product = ProductModelForm(initial={'name':'iphone XS'})
        return render(request, 'data_form.html',locals())
    else:
        product = ProductModelForm(request.POST)
        if product.is_valid():
            #获取并清洗weight的数据
            weight = product.cleaned_data['weight']
            #数据保存方法1:直接保存到数据库
##            product.save()
            #数据保存方法2:save方法设置commit=False,将生成数据库对象product_db,
            #然后对该对象的属性值修改并保存
            product_db = product.save(commit=False)
            product_db.name = '我的 iphone'
            product_db.save()
            #数据保存方法3:save_m2m()保存ManyTOMany的数据模型
##            product.save_m2m()
            return HttpResponse('提交成功!weight清洗后的数据是:'+weight)
        else:
            #将错误信息以json格式输出
            error_msg = product.errors.as_json()
            print(error_msg)
            return render(request, 'data_form.html', locals())

函数model_index的处理逻辑和上面的index函数大致相同:

  • 首先判断用户的请求方式,不同的请求方式做不同的处理。
  • 若是GET请求,函数根据URL传递的变量id来查找模型Product的数据,如果数据存在,以参数的形式传递给表单ProductModelForm的参数instance,在生成网页时,模型数据会填充到对应的元素控件上,如下图: image.png
  • 若时POST请求,函数首先对表单数据进行验证,如果验证失败,返回失败信息;验证成功,则使用cleaned_data方法对字段weight进行清洗,最后将表单数据保存到数据库。
    在以上代码中,还存在一点小问题,比如产品类型的下拉框数据是模型Type对象,如果要将模型Type的字段type_name的数据在下拉框中展示,可以通过定义模型或定义表单两方面解决:
定义模型是在定义模型Type时,设置该模型的返回值。
#models.py
class Type(models.Model):
    id = models.AutoField(primary_key=True)
    type_name = models.CharField(max_length=20)
    #设置返回值,若不设置,则默认返回Type对象
    def __str__(self):
        return self.type_name

再运行一次刚才的网址,可以看到产品类型已经变成了名称。

image.png
如果存在多个下拉框,而且每个下拉框的数据分别取同一个模型的不同字段,那么可以在定义表单类的时候重写初始化函数__init__()
#forms.py 
class ProductModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ProductModelForm, self).__init__(*args, **kwargs)
        #设置下拉框的数据
        type_obj = Type.objects.values('type_name')
        choices_list = [(i+1, v['type_name']) for i,v in enumerate(Type.objects.values('type_name'))]
        self.fields['type'].choices = choices_list
        #初始化字段name
        self.fields['name'].initial = '我的手机'
表单初始化的方法有以下几种:
  • 在视图函数中对表单实例化时,设置实例化对象的参数initial。如product = ProductModelForm(initial={'name':'iphone XS'}),参数值以字典格式表示,字典键为表单的字段名,该方法适用于所有表单类。
  • 表单类中进行实例化时,如果初始化的数据是一个模型对象的数据,可设置参数instance,如product = ProductModelForm(instance=instance[0]),该方法只适用于ModelForm.
  • 定义表单字段时,对表单字段设置初始化参数initial,此方法不适用于ModelForm,如name = forms.CharField(initial='我的手机')。
  • 重写表单类的初始化函数 __init__,适用于所有表单类,如在初始化函数中设置slef.fields['name'].initial='我的手机'.

数据库的保存实际上只有save()和save_m2m()方法实现。save()只适合将数据保存在非多对多关系的数据表,而save_m2m只适合将数据保存在多对多关系的数据表。

相关文章

  • (8)Django - 表单

    表单是搜集用户数据信息,实现网页数据交互的关键。Django的表单功能由Form类实现,主要分两种:django....

  • Django表单(二)

    什么是django表单 django中的表单不是html中的那个表单,这里是指django有一个组件名叫表单 它可...

  • Django表单

    Django表单 一、构建表单 1.直接构建表单 2.Django构建表单 (1)Form 类 forms.py ...

  • Forms#-表单基础

    在Django中创建表单 表单# forms.pyfrom django import formsclass Na...

  • 10 Form表单

    一:什么是Form?什么是Django Form Django的表单系统中,所有的表单类都作为django.for...

  • Django ModelForm and Form

    django表单系统中,所有的表单类都作为django.forms.Form的子类创建,包括ModelForm 关...

  • Django表单使用介绍

    Django版本:2.1.3Django表单非常强大,熟悉后可以大大提高工作效率。 Django表单分为两种使用方...

  • Django 学习日记 - Form- setp12

    1 概念 django表单系统中,所有的表单类都作为django.forms.Form的子类创建,包括ModelF...

  • 02 通过留言版功能回顾django基础知识

    一、django目录介绍 二、配置表单页面 三、django orm介绍与model设计

  • Form类学习笔记

    Django的表单功能由Form类实现 分为两种 django.forms.Form 和 Django.forms...

网友评论

      本文标题:(8)Django - 表单

      本文链接:https://www.haomeiwen.com/subject/txajpqtx.html