一、什么是Form?什么是Django Form?
django表单系统中,所有的表单类都作为django.forms.Form的子类创建,包括ModelForm
关于django的表单系统,主要分两种
基于django.forms.Form:所有表单类的父类
基于django.forms.ModelForm:可以和模型类绑定的Form
实例:
实现添加出版社信息的功能
以下表单都是基于如下models.py中的Publisher类实现的:
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30, verbose_name="名称")
address = models.CharField("地址", max_length=50)
city = models.CharField('城市',max_length=60)
state_province = models.CharField('省份',max_length=30)
country = models.CharField('国家',max_length=50)
website = models.URLField('网址',)
class Meta:
verbose_name = '出版商'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class Author(models.Model):
name = models.CharField(max_length=30)
class AuthorDetail(models.Model):
sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),))
email = models.EmailField()
address = models.CharField(max_length=50)
birthday = models.DateField()
author = models.OneToOneField(Author)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
price = models.DecimalField(max_digits=5,decimal_places=2,default=10.00)
二、不使用Django Form的情况(原生的写html实现表单提交)
add_publisher.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加出版社信息</title>
</head>
<body>
<form action="{% url 'add_publisher' %}" method="post">
{% csrf_token %}
名称:<input type="text" name="name"><br>
地址:<input type="text" name="address"><br>
城市:<input type="text" name="city"><br>
省份:<input type="text" name="state_province"><br>
国家:<input type="text" name="country"><br>
网址:<input type="text" name="website"><br>
<input type="submit" value="添加"><br>
</body>
</html>
views中的控制语句:
from django.shortcuts import render,render_to_response,redirect
from django.contrib.auth.models import User
from django.http import HttpRequest,HttpResponse
from django.template import loader
import datetime
from hello.models import Publisher
def add_publisher(request):
if request.method == "POST":
#不使用Form情况时,如果为post提交,去接收用户提交过来的数据
print(request.POST)
return HttpResponse("添加信息成功")
# 使用django Form表单提交
else:
return render(request,'add_publisher.html',locals())
启动服务
查看功能页面:
比如提交如下内容:
控制端查看提交结果:
发现提交结果以字典的方式显示如下:
<QueryDict: {'state_province': ['北京'], 'website': ['http://www.tup.tsinghua.edu.cn'], 'address': ['北京市xxx路'], 'country': ['中国'], 'name': ['清华大学出版社'], 'csrfmiddlewaretoken': ['vkk9K0fT0FATXoIHQ5El2OLyULJUszrs'], 'city': ['北京市']}>
接下来修改views.py中add_publisher类将提交内容写入数据库:
from django.shortcuts import render,render_to_response,redirect
from django.contrib.auth.models import User
from django.http import HttpRequest,HttpResponse
from django.template import loader
import datetime
from hello.models import Publisher
def add_publisher(request):
if request.method == "POST":
#不使用Form情况时,如果为post提交,去接收用户提交过来的数据
name = request.POST['name']
address = request.POST['address']
city = request.POST['city']
state_province = request.POST['state_province']
country = request.POST['country']
website = request.POST['website']
#将提交的结果写入数据库
Publisher.objects.create(
name=name,
address=address,
city=city,
state_province=state_province,
website=website,
)
#print(request.POST)
return HttpResponse("添加信息成功")
# 使用django Form表单提交
else:
return render(request,'add_publisher.html',locals())
添加测试内容:
添加测试内容之前:
提交上述内容之后:
提交页面显示提交成功,再看下数据库:
发现提交内容被写入数据库,说明提交内容可以正常写入数据库,那如果提交内容为空,即什么都不写呢?
添加--->
再看下数据库:
发现多了一条空内容,即提交的时候全为空也可以提交成功并写入数据库。
所以这里明显一个缺陷是少了一个验证功能,即提交内容不能全为空,如果为空则提示必须填写相关项,接下来使用Form来实现表单的验证功能。
三、使用Form的情况
如果使用Form的话需要在我们的app里面添加一个模块,这里将模块命名为forms.py,内容如下
from django import forms
class PublisherForm(forms.Form):
name = forms.CharField()
address = forms.CharField()
city = forms.CharField()
state_province = forms.CharField()
country = forms.CharField()
website = forms.URLField()
注释:PublisherForm类继承django中的forms模块下的Form类,从而成为Form类的子类,Form类中的所有属性PublisherForm类中都会继承下来,所以就可以通过forms类的相关字段来定义的name、address、city、state_province、country、website属性。
接下来在models.py中导入定义的forms.py。
from hello.forms import PublisherForm
然后在views.py中对定义的PublisherForm类进行实例化成对象publisher_form,实现代码入下:
from django.shortcuts import render,render_to_response,redirect
from django.contrib.auth.models import User
from django.http import HttpRequest,HttpResponse
from django.template import loader
import datetime
from hello.models import Publisher
from hello.forms import PublisherForm
def add_publisher(request):
if request.method == "POST":
# 使用django Form表单提交
pass
else:
publisher_form = PublisherForm() #定义一个实例来接收初始化的PublisherForm类
return render(request,'add_publisher.html',locals())
当实现了实例化对象publisher_form之后,name在模板中就可以接收实例化对象,然后显示到页面,实现代码入下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加出版社信息</title>
</head>
<body>
<form action="{% url 'add_publisher' %}" method="post">
{% csrf_token %}
{{ publisher_form.as_p }}
<input type="submit" value="添加"><br>
</body>
</html>
这里就可以通过实例化的对象publisher_form来显示,这里不需要再单独对应name、city、address等属性字段了,另外通过publisher_form对象显示的方式有多种,比如as_p、as_table、as_ul。这里用as_p为例,刷新页面
进一步查看源代码:
发现都是用p标签来进行分界各个字段属性,另外通过id来标识区分各个字段的名称,比如id=id_name,id=id_address等,name的值则等于views.py从forms.py中定义的对应名称值。
接下来解决提交时验证的问题,代码如下:
def add_publisher(request):
if request.method == "POST":
# 使用django Form表单提交
publisher_form = PublisherForm(request.POST)#通过request.POST传过来的数据将表单PublisherForm初始化以后再赋给publisher_form生成一个带有提交对象的表单
if publisher_form.is_valid():#对提交过来的对象内容进行验证
Publisher.objects.create(
name= publisher_form.cleaned_data['name'],
address = publisher_form.cleaned_data['address'],
city = publisher_form.cleaned_data['city'],
state_province = publisher_form.cleaned_data['state_province'],
website = publisher_form.cleaned_data['website'],
)
return HttpResponse("添加信息成功")
else:
publisher_form = PublisherForm() #定义一个实例来接收初始化的PublisherForm类
return render(request,'add_publisher.html',locals())
刷新请求页面如下所示:
点击提交出现如下页面:
说明字段内容为必须填写,为空字段是不允许提交。填写相关信息继续提交:
点击添加:
提示网址不符合规范。修改后继续提交:
再添加:
查看数据库:
这样就添加成功提交数据。
通过这个form类,虽然增加了一个form.py文件,但是从整体上看代码是简化了,不用去一个一个的写字段规范验证方法,同时模板里也相对以前原生html要简化很多,又提高了代码 的可读性
对于界面友好度,默认显示如下:
我们可以通过一个中文显示标签(label)来定义,具体实现在forms,py中添加:
from django import forms
class PublisherForm(forms.Form):
name = forms.CharField(label='名称')
address = forms.CharField(label='地址')
city = forms.CharField(label='城市')
state_province = forms.CharField(label='省份')
country = forms.CharField(label='国家')
website = forms.URLField(label='网址')
刷新页面:
提交数据不符合规范时的提示信息“This field is required.” 也可以修改:
同样在forms.py中每个字段实例化时都有一个错误信息的属性:error_messages,这里我们可以根据实际情况对该属性的值进行重新定义
from django import forms
class PublisherForm(forms.Form):
name = forms.CharField(label='名称',error_messages={"required":"该项为必填项"})
address = forms.CharField(label='地址',error_messages={"required":"该项为必填项"})
city = forms.CharField(label='城市',error_messages={"required":"该项为必填项"})
state_province = forms.CharField(label='省份',error_messages={"required":"该项为必填项"})
country = forms.CharField(label='国家',error_messages={"required":"该项为必填项"})
website = forms.URLField(label='网址',error_messages={"required":"该项为必填项"})
刷新页面:
也可以对相应字段长度进行定义,如果超出了就提示对应的信息。比如对名称长度定义为100,超出长度则提示"名称太长"
from django import forms
class PublisherForm(forms.Form):
name = forms.CharField(label='名称',max_length=100,error_messages={"required":"名称太长不符合规范"})
address = forms.CharField(label='地址',error_messages={"required":"该项为必填项"})
city = forms.CharField(label='城市',error_messages={"required":"该项为必填项"})
state_province = forms.CharField(label='省份',error_messages={"required":"该项为必填项"})
country = forms.CharField(label='国家',error_messages={"required":"该项为必填项"})
website = forms.URLField(label='网址',error_messages={"required":"该项为必填项"})
四、使用ModelForm的情况
使用ModelForm时在定义PublisherForm时候直接继承它,实现代码如下:
forms.py
from django import forms
from hello.models import Publisher
class PublisherForm(forms.ModelForm):
class Meta:
model = Publisher
exclude = ("id",)
views.py
def add_publisher(request):
if request.method == "POST":
#使用ModelForm情况
publisher_form = PublisherForm(request.POST)
if publisher_form.is_valid():
publisher_form.save()#插入数据操作语句
return HttpResponse("添加信息成功")
else:
publisher_form = PublisherForm() # 定义一个实例来接收初始化的PublisherForm类
return render(request, 'add_publisher.html', locals())
刷新页面:
添加提交内容:
提交结果:
查看数据库添加的数据:
总结:
使用Django中的Form可以大大简化代码,常用的表单功能特性都整合到了Form中,而ModelForm可以和Model进行绑定,更进一步简化操作
查看forms相关资料:
https://docs.djangoproject.com/en/1.9/ref/forms/api/
https://docs.djangoproject.com/en/1.9/ref/forms/fields/
五、Form表单的验证方法
1、表单字段的验证器
https://docs.djangoproject.com/en/1.9/ref/validators/
forms.py代码实现如下:
from django import forms
from hello.models import Publisher
from django.core.exceptions import ValidationError
#表单字段的验证器
def validate_name(value):
try:
Publisher.objects.get(name=value)
raise ValidationError("%s的信息已经存在"% value)
except Publisher.DoesNotExist:
pass
class PublisherForm(forms.ModelForm):
name = forms.CharField(label="名称",validators=[validate_name])
class Meta:
model = Publisher
exclude = ("id",)
class PublisherForm(forms.ModelForm):
name = forms.CharField(label="名称",validators=[validate_name])
class Meta:
model = Publisher
exclude = ("id",)
操作演示:
以name=“南京大学出版社”为例
提交信息中名称"南京大学出版社"
2、clean_filedname,验证字段,针对某个字段进行验证
forms.py代码实现方式:
from django import forms
from hello.models import Publisher
from django.core.exceptions import ValidationError
def clean_name(self):
value = self.cleaned_data.get('name')#表单对象self自己通过cleand_data属性调用get方法获取name的值然后赋给value
try:
Publisher.objects.get(name=value)#将上面的value值通过Publisher.objects.get进行判断
raise ValidationError("%s的信息已经存在" % value)
except Publisher.DoesNotExist:
pass
return value #在没有出错的情况下将原有的值返回回去,否则表单无法获取值
class Meta:
model = Publisher
exclude = ("id",)
同样以"name=南京大学出版社"为例
3、表单clean方法,可针对整个表单进行验证
forms.py实现代码:
from django import forms
from hello.models import Publisher
from django.core.exceptions import ValidationError
#通过clean对整个表单进行验证
def clean(self):
cleaned_data = super(PublisherForm,self).clean()#获取父类中的方法clean()
value = cleaned_data.get('name')
try:
Publisher.objects.get(name=value)
self._errors['name']=self.error_class(["%s的信息已经存在"%value])#指定不能重复的字段名称
except Publisher.DoesNotExist:
pass
return cleaned_data #返回整个表单的值
class Meta:
model = Publisher
exclude = ("id",)
提交空内容:
同样以name=南京大学出版社为例
注释:代码中指定了对name字段就行验证,所以name值存在了就提示内容已经存在。