ModelForm组件是用于简化表单操作的组件,学习ModelForm组件最好的办法是通过与原生的Form表单进行对比,即可以体会到ModelForm的便利。
1、原生方式
使用原生的方式实现Form表单提交功能。以新建用户为例,首先在user_add.html页面中编写用户信息表单。
{% extends 'menu_template.html' %}
{% block menu %}
{# 第二步:新建用户内容区 #}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">新建用户</div>
<div class="panel-body">
{# 表单 #}
<div class=col-md-10>
<form class="form-horizontal" method="post">
{% csrf_token %}
{# 姓名 #}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">姓名</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputEmail3" placeholder="姓名" name="name">
</div>
</div>
{# 密码 #}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">密码</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputEmail3" placeholder="密码" name="password">
</div>
</div>
{# 年龄 #}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">年龄</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputEmail3" placeholder="年龄" name="age">
</div>
</div>
{# 余额 #}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">余额</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputEmail3" placeholder="余额" name="account">
</div>
</div>
{# 入职时间 #}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">入职时间</label>
<div class="col-sm-10">
<input type="text" class="form-control" id="inputEmail3" placeholder="入职时间" name="create_time">
</div>
</div>
{# 性别 #}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">性别</label>
<div class="col-sm-10">
<select name="gender" class="form-control">
<option value="1">男</option>
<option value="2">女</option>
</select>
</div>
</div>
{# 所属部门 #}
<div class="form-group">
<label for="inputEmail3" class="col-sm-2 control-label">所属部门</label>
<div class="col-sm-10">
<select name="depart" class="form-control">
<option value="0">请选择部门</option>
{% for obj in user_info %}
<option value="{{ obj.depart_id }}">{{ obj.depart.title }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">保 存</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
在views.py文件编写user_add()函数,用于添加用户。
# 添加用户
def user_add(request):
# 1、将请求转交到user_add.html页面
if request.method == "GET":
# 获取用户相关的信息
user = models.UserInfo.objects.all()
# 接收到请求后,跳转到用户添页面
return render(request, "user_add.html", {"user_info": user})
# 2、获取用户提交的数据
if request.method == "POST":
name = request.POST.get("name")
password = request.POST.get("password")
age = request.POST.get("age")
account = request.POST.get("account")
create_time = request.POST.get("create_time")
gender = request.POST.get("gender")
depart = request.POST.get("depart")
print(name, password, age, account, create_time, gender, depart)
# 3、将获取到的数据添加到数据库
models.UserInfo.objects.create(name=name, password=password, age=age, account=account, create_time=create_time, gender=gender, depart_id=depart)
# 4、数据插入后跳转到用户列表页面
return redirect("/user/list/")
在urls.py文件添加访问新建用户的URL地址。
# 新建用户
path('user/add/', views.user_add)
不采用原始方式的本质是它比较麻烦。
① 用户提交的数据没有校验。原始方式需要拿到字段一个一个校验,校验工作量非常的大。
② 错误提示,如果出现错误,页面应该出现错误提示。
③ 页面上的每一个字段都需要我们重新写一遍,很费劲。
④ 关联的数据,需要手动获取,然后才能展示在展示在页面中。
Django中提供ModelForm组件帮助我们完善以上问题。
2、ModelForm组件
基于ModelForm组件实现新增用户功能。
ModelForm组件是最常用的表单组件。以后在Django中,只要涉及表单提交,就得使用ModelForm组件进行操作,它主要解决以下功能。
① 它可以直接将数据库中的数据给展示到HTML页面。
② 它可以对提交的数据进行校验。
③ 如果提交数据有误,提交后ModelForm组件不会清楚之前填写的数据,它会保留原来的数据,以便后期修改。
2.1 添加新增用户的URL地址
# 基于ModelForm的新增用户
path('user/model/form/add/', views.user_model_form_add),
2.2 在views.py文件中添加user_model_form_add()函数,并编写逻辑。
2.2.1 创建ModelForm类,并设置相关规则
# 使用ModelForm前,需要创建一个类,并继承ModelForm
from django import forms
# 创建一个自定义的ModelForm类,并继承ModelForm
class UserModelForm(forms.ModelForm):
# ****** 定制校验规则 ******
# Form中,自动校验是否为空,如果需要其它校验,需要重新定义
# 例子追加了校验规则,对name字段进行校验,要求字符长度最小不低于2
name = forms.CharField(label="用户名", min_length=2)
# 第一步:设置Meta类
# 每个模型类(Model)下,都有一个子类(Meta),这个子类就是用于定义元数据的类。
# Meta类中封装了一些数据库的信息(称之为Model的元数据),Django会将Mate中的元数据选项定义附加到Model中,
# 常见元数据据定义有db_table(数据表名称)、abstract(抽象类)、ordering(字段排序)等。
class Meta:
# 第二步:设置关联的类
model = models.UserInfo
# 第三步:设置相关字段
# fields是设置字段名称列表,包括返回字段中的命名字段;如果省略则返回全部字段。
fields = ["name", "password", "age", "account", "create_time", "gender", "depart"]
# 第四步:给关联字段生成的HTML标签设置属性
# widgets用于设置映射在字段上的HTML属性
# 1、手动为每个字段添加样式;可以在Django后台,定制控件,用于设置生成HTML时的属性。
# widgets = {
# "name": forms.TextInput(attrs={"class": "form-control"}),
# "password": forms.PasswordInput(attrs={"class": "form-control"}),
# "age": forms.TextInput(attrs={"class": "form-control"}),
# "account": forms.NumberInput(attrs={"class": "form-control"}),
# "create_time": forms.TextInput(attrs={"class": "form-control"}),
# "gender": forms.TextInput(attrs={"class": "form-control"}),
# "depart": forms.TextInput(attrs={"class": "form-control"}),
# }
# 为简化第四步,可以使用重写构造函数的方式设置自动生成的HTML标签属性
def __init__(self, *args, **kwargs):
# 调用父类构造函数
super().__init__(*args, **kwargs)
# 2、循环为每个字段添加样式
# 通过循环找到所有字段中的插件,并给所有字段加上class属性,就不需要手动的在widgets控件中一个一个的添加。
# self.field.items():获取字段列表中所有元素
for name, field in self.fields.items():
field.widget.attrs = {
"class": "form-control",
"placeholder": field.label
}
2.2.2 编写添加新用户逻辑
# 基于ModelForm组件添加新用户
def user_model_form_add(request):
# 第一步:跳转到user_model_form_add.html(添加用户页面),并携带个字段的属性
if request.method == "GET":
# 在使用Form表格数据时,需要实例化ModelForm类,得到的Form对象即包含数据库中的数据。ModelForm类创建见2.2.1小节。
form = UserModelForm()
# 将获取到的数据,返回给user_model_form_add.html页面
return render(request, "user_model_form_add.html", {"form": form})
# 第二步:使用自定义的ModelForm类的实例,获取请求的数据
# 接收前台提交的POST请求,然后进行校验
# data=request.POST将请求数据与ModelForm类进行高度封装,从而获取Form表单对象
form = UserModelForm(data=request.POST)
# 第三步:判断请求数据的有效性
if form.is_valid():
print("获取POST提交的数据:", form.cleaned_data)
# 第四步:如果有效,将数据保存到数据库
# Meta类中提供save()方法,用于将请求数据保存到映射数据库中
form.save()
return redirect("/user/list/")
else:
# 第四步:如果无效,将Form表单数据再次返回给user_model_form_add.html页面,返回的数据中携带有错误信息。
return render(request, "user_model_form_add.html", {"form": form})
2.3 在user_model_form_add.html页面中设置Form表单,用于收集数据和展示后台数据。
2.3.1 设置user_model_form_add.html页面
{% extends 'menu_template.html' %}
{% block menu %}
{# 内容区 #}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">新建部门</div>
<div class="panel-body">
{# 表单 #}
<div class=col-md-10>
{# 注意:使用ModelForm后,浏览器页面为我们提供表单自动校验功能,如果要自定义校验错误需要关闭浏览器自动校验,使用novalidate(关闭校验)即可 #}
<form class="form-horizontal" method="post" novalidate>
{% csrf_token %}
{# form是后台传入的数据集,通过循环获取里面的每个对象 #}
{% for field in form %}
<div class="form-group">
{# 使用xx.label可以获取标题,即models.py文件中定义的verbose_name属性,例如:verbose_name='姓名' #}
<label for="inputEmail3" class="col-sm-2 control-label">{{ field.label }}</label>
{# 使用field则可以获取每个字段的值。但是,在获取关联表中的字段时它获取的是关联表中每条数据的对象,要想获取字段需要重写str函数,详见2.3.2小节 #}
<div class="col-sm-10">
{{ field }}
{# <input type="text" class="form-control" id="inputEmail3" placeholder="{{ field.lable }}" name="title"> #}
{# 浏览器器为自动校验错误信息。如果想要获取错误信息,把它展示到HTML页面,可以使用errors属性获取,它封装了所有错误信息的数据集,如果要实现自已获取错误信息,需要关闭浏览器自动校验功能 #}
<span style="color: red">
{# 默认只能校验是否为空 #}
{# errors返回值为列表,我们只需要获取第1个错误即可 #}
{# 错误信息默认以英文形式显示,如果要显示中文,需要到settings.py中设置LANGUAGE_CODE = 'zh-hans',详见2.3.3小节 #}
{{ field.errors.0 }}
</span>
</div>
</div>
{% endfor %}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">保 存</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
2.3.2 设置表模型
在models.py文件中,重写模型类的str函数,将需要的字段返回给调用者。
from django.db import models
# 部门表
class Department(models.Model):
# verbose_name用于给字段进行备注
title = models.CharField(verbose_name='标题', max_length=32)
# 重写Department类的str方法,用于设置实例化对象时返回的数据(默认返回当前对象)
def __str__(self):
return self.title
2.3.3 设置中文输出
在settings.py文件中将LANGUAGE_CODE属性的值修改为zh-hans,则可以实现错误信息以中文方式显示。
LANGUAGE_CODE = 'zh-hans'