Django世界-迈出第三步-模型与表单

基础表单django.forms.Form

表单介绍

HTML表单负责接收用户的输入,对输入进行合法格式判断,并将数据发送到服务器。一个HTML表单必须指定两样东西:发送数据的url地址,发送的HTTP方法(GET、POST)。

类型说明
GET方法将用户数据以?<键1>=<值1>&<键2>=<值2>&…形式拼接到url后面,通常用于请求数据、网页搜索的表单。
POST方法组合表单的数据并进行编码,然后打包发送到服务器,数据不会出现在url中,通常用于保密信息的发送、大量数据的表单、二进制数据。

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

在input标签中
对于form表单也是一样,如果前端不指定post方法,默认使用get方法。
首先在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的

的形式展现在网页上

//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>

表单字段(常用)

  1. CharField

渲染类型:TextInput
输入空值的返回:empty_value设定的值
转换为对象:Unicode对象
验证:min_length,max_length(如果设置过)
可用的错误信息键:min_length,max_length,required
max_length:允许输入的最大长度
min_length:允许输入的最小长度
strip:默认为True,表示去除前后的空格
empty_value:输入为空时返回的值,默认为空字符串

  1. EmailField

渲染类型:EmailInput
输入空值的返回:空字符串
转换为对象:Unicode对象
验证:用正则表达式验证表单的值,必须是合法邮件地址
可用的错误信息键:required,invalid
max_length:允许输入的最大长度
min_length:允许输入的最小长度

  1. 更多
字段说明
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地址

表单字段的参数

error_messages

error_messages={‘key’: ‘value’}
自定义错误提示信息,键是表单里同名的错误类型字符串,值是自定义的提示信息字符串。
其中的key包括:
required,无填写内容
invalid,非法字符
maxlength,超出长度上限

max_length
最大长度

min_length
最小长度
required

required=True/False
该字段必填/非必填。
当为True时,<表单对象>.clean(‘‘)和<表单对象>.clean(None)返回ValidationError异常。
当为False时,<表单对象>.clean(‘‘)和<表单对象>.clean(None)返回None。
label

label=’<显示值>’
字段渲染成HTML代码的提示词,默认在后面加冒号。
如字段name = forms.CharField(label=’Your Name’),显示为HTML的

label_suffix

label_suffix=’<字符>’
自定义字符,代替label生成显示值后面的冒号。
initial

initial=’<值>’
为表单元素定义初始值,即标签中的value=,可传入值和对象。

initial不会作为数据提交,表单仍需要用户填写数据。
只能用initial传递初始值,如果在渲染表单的时候传递一个初始值字典,会触发表单的验证,此时输出的HTML页面可能包含验证错误。

widget

widget=<widget类>
指定渲染Widget时使用的widget类,即前端中的type类型。
可用attr参数传入一个字典,对widget添加CSS样式,如:
name = forms.CharField(widget=forms.TextInput(attrs={‘class’: ‘special’}) )
comment = forms.CharField(widget=forms.TextInput(attrs={‘size’: ‘40’}) )
help_text

help_text=’<文本>’
设定辅助性描述文本,相当于HTML的默认显示值。

validators

validators=[ ]
传入列表,包含对字段验证的函数,自定义验证方法的时候用。

localize

实现表单数据输入的本地化。

disabled

disabled=True/False
设置对字段禁用/启用编辑,禁用编辑后,即使非法篡改前端页面的属性向服务器提交,该字段值也会被忽略。

参数:

参数说明
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_suffixLabel内容后缀,在Label后添加内容

对 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())
  1. 用户首次访问url地址时,等于向项目发送一个GET请求,函数index将表单ProductForm实例化并传递给模板,由模板引擎生成HTML表单返回给用户。
  2. 当用户在表单中输入相关信息并提交时,等于向项目发送一个POST请求,函数index首先获取表单数据对象product,然后由is_valid()方法对数据对象product进行数据验证。
  3. 如果验证成功,可以使用product[‘name’]或prodcut.cleaned_data[‘name’]方法来获取用户在某个控件上的输入值,实现表单和模型的信息交互。
    下面来验证一下是否能正常工作,在姓名栏输入了空格
    在这里插入图片描述
    验证正常工作了。
  4. 在上述例子中,模板data_form.html的表单是使用HTML的标签展现在网页上,除此之外,表单还可以用其他HTML标签展现,只需将模板data_form.html的对象product使用以下方法即可生成其他HTML标签:
参数说明
{{ product.as_ul }}将表单生成HTML的 ul 标签
{{ product.as_p }}将表单生成HTML的 p 标签
{{ product.type }}生成单个HTML元素控件
{{ product.type.label }}获取表单字段的参数 label 属性值

数据表单django.forms.ModelForm

数据表单(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())
            

如果存在多个下拉框,而且每个下拉框的数据分别取同一个模型的不同字段,那么可以在定义表单类的时候重写初始化函数__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 = '我的手机'

表单初始化

表单初始化的方法有以下几种:

  1. 在视图函数中对表单实例化时,设置实例化对象的参数initial。如product = ProductModelForm(initial={‘name’:‘iphone XS’}),参数值以字典格式表示,字典键为表单的字段名,该方法适用于所有表单类。
  2. 在表单类中进行实例化时,如果初始化的数据是一个模型对象的数据,可设置参数instance,如product = ProductModelForm(instance=instance[0]),该方法只适用于ModelForm.
  3. 定义表单字段时,对表单字段设置初始化参数initial,此方法不适用于ModelForm,如name = forms.CharField(initial=‘我的手机’)。
  4. 重写表单类的初始化函数 init,适用于所有表单类,如在初始化函数中设置slef.fields[‘name’].initial=‘我的手机’.

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值