Django — 如何优雅的提交表单
- 2020 年 1 月 19 日
- 筆記
前言
前面的内容我们基本上以 get
请求作为例子,那 post
请求Django是如何处理的呢?本章内容我们就来介绍Django如何发起和处理 post
请求的。
一个简单的表单
就拿我们的Cat类来说,之前新增Cat对象都是通过admin后台或者数据库API来操作,现在我们尝试通过前端页面来添加Cat对象。
模板
首先我们新建一个简单的新增Cat对象的模板,新建 'demo_app/add.html'
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/html"><head> <meta charset="UTF-8"> <title>add cat</title></head><body> <form action="{% url 'demo_app:add' %}" method="post"> {% csrf_token %} name:<input name="name"> </br> age :<input name="age" ></br> <input type="submit" value="submit"> </form> </body></html>
- 在html文件中,我们定义了一个Form 表单,提交的这个表单会改变服务端的数据,所以我们将
method="post"
,并且我们将action 设置为{%url'demo_app:add'%}
, 这表明了了我们会像demo_app/views
中的 add方法发送数据。 {%csrf_token%}
是Django 用来防止跨站点请求伪造。Django 内部的POST表单都要如此。
视图
我们还要创建一个视图来实现这个新增cat 的功能。将下面的代码添加到 deom_app/views.py
文件中
def add(request): if request.method == 'GET': return render(request, 'demo_app/add.html',{}) if request.method == 'POST': name = request.POST['name'] age = int(request.POST['age']) cat = Cat(name=name, age=age) cat.save() cats_list = Cat.objects.all() return render(request,'demo_app/index.html',{'cats_list':cats_list})
request.method
表示该请求的方法类型,是 GET 还是 POST,或者其他 如 PUT ,DELETE等类型。ifrequest.method=='GET':returnrender(request,'demo_app/add.html',{})
表示如果是GET请求时,则系统跳转到 add 页面(比较简陋)

request.POST
是一个类字典对象,让你可以通过关键字的名字获取提交的数据,需要注意的是,返回的值永远是字符串。- 当
request.method=='POST'
时 (表示 如果是 POST 请求时),则取出请求中的name 和 age 关键字的 数据, 新增对象,并直接返回index页面(list页面)
URL配置
修改 demo_proj/urls.py
文件
from django.contrib import adminfrom django.conf.urls import includefrom django.urls import path urlpatterns = [ path('admin/', admin.site.urls), path('demo_app/', include(('demo_app.urls','demo_app'), namespace='demo_app'))]
修改 demo_app/urls.py
文件
urlpatterns = [ path('', views.index, name='index'), path('hello/<str:country_name>/', views.hello_country, name='hello'), path('add/', views.add, name='add'),]
- 因为我们指定了Form表单的
action
为{%url'demo_app:add'%}
,所以要注意include 中的写法,如果include
方法中namespace
为其他值,如demo_app1
,那么action
中因为{%url'demo_app1:add'%}
经过上面的配置,我们就能演示从前端新增cat 对象功能了。我们首先跳转到add 页面

点击提交,页面跳转至 list 页面

Form进阶
功能是实现了,但比较简陋,还不完善。就比如,字段是否必填,字段的长度是否超出最大范围,为避免这样的错误,我们必须对字段进行校验,那么我们就在views.py 文件中加上 校验的业务逻辑代码,如下所示:
def add(request): if request.method == 'GET': return render(request, 'demo_app/add.html',{}) elif request.method == 'POST': name = request.POST['name'] age = int(request.POST['age']) # 新增字段校验逻辑 if name ...: pass else: pass cat = Cat(name=name, age=age) cat.save() cats_list = Cat.objects.all() return render(request,'demo_app/index.html',{'cats_list':cats_list})
这样固然能达到效果,但如果字段很多的话,代码将非常臃肿,弊端很大。Django 为此提出了一种较为简便的方法Form ,Django 中的表单有一下两个作用:
- 渲染表单模板
- 验证数据是否合法
下面我们来介绍下他的使用。
- 在相应的App下新建
forms.py
文件 在demo_app
目录下新建froms.py
文件
from django.forms import forms class AddFrom(forms.Form): name = forms.CharField(required=True, max_length=10, label='name_form') age = forms.IntegerField(required=True)
name=forms.CharField(required=True,max_length=10,label='name_form')
定义了name
这个字段是字符串类型的,且是必填的,最大长度为10,label='name_form'
的作用是渲染html 表单中字段为name
的label
为name_form
。age=forms.IntegerField(required=True)
定义了age
为个整数类型的字段,且是必填的。
- 修改
views.py
文件 修改demo_app/views.py文件
(为区分根之前版本的不同,我将之前版本的注释掉,以便比较)
def add(request): if request.method == 'GET': # return render(request, 'demo_app/add.html',{}) form = AddFrom() return render(request, 'demo_app/add.html',{'form':form}) elif request.method == 'POST': # name = request.POST['name'] # age = int(request.POST['age']) # cat = Cat(name=name, age=age) # cat.save() # cats_list = Cat.objects.all() # return render(request, 'demo_app/index.html', {'cats_list': cats_list}) form = AddFrom(request.POST) if form.is_valid(): name = request.POST['name'] age = int(request.POST['age']) cat = Cat(name=name, age=age) cat.save() cats_list = Cat.objects.all() return render(request,'demo_app/index.html',{'cats_list':cats_list})
- 当请求是 GET 请求时,新建了一个
AddFrom
实例,并将它作为上下文传递给前端,这样就达到了渲染表单的效果。 - 当请求是 POST请求时,我们再次创建了一个
AddFrom
实例 并用请求数据填充它form=AddFrom(request.POST)
,我们称作为 数据绑定表单。 form.is_valid()
表示对POST请求中的数据按照当时定义表单字段时定义的规则校验。如age=forms.IntegerField(required=True)
就会对 请求中的 nage 为 age 的字段进行校验,判断它是否为必填(是否传了),只有所有字段都通过校验后才能进行下面的逻辑。- 这边代码只是 演示作用,没有写
is_valid()
不通过的情况,代码可以大家继续完善。
- 修改模板文件 修改
templates/dem_app/add.html
文件,(为区分根之前版本的不同,我将之前版本的注释掉,以便比较)
<!DOCTYPE html><html lang="en" xmlns="http://www.w3.org/1999/html"><head> <meta charset="UTF-8"> <title>add cat</title></head><body> <form action="{% url 'demo_app:add' %}" method="post"> {% csrf_token %} {{ form }} {# name:<input name="name"> </br>#} {# age : <input name="age" ></br>#} <input type="submit" value="submit"> </form> </body></html>
- 修改过后的我们可以看到,在
<form>
标签中,只有{{form}}
了,这就是 渲染表单模板 的作用
我们运行程序可以到同样的效果,大家也可以试试当输入的 name
或 age
不符合条件的情况系统会是什么反应。

我们一般推荐不用表单渲染,因为样式不受自己控制,另外当我们 is_valid()
返回true 后,我们可以通过 cleaned_data
属性中找到所有通过验证的表单数据,这个大家可以自己探索下。