什么是django表单
- django中的表单不是html中的那个表单,这里是指django有一个组件名叫表单
- 它可以通过配置去验证数据的合法性
- 同样也可以通过配置生成HTML代码.
使用表单:
- 创建一个
forms.py
的文件,放在指定的app当中,然后在里面写表单。 - 如果新建了app应用,同样要记得在settings中添加至INSTALLED_APPS。
- 表单是通过类实现的,继承自`forms.Form,然后在里面定义要验证的字段。
from django import forms
- 在表单中,创建字段跟模型是一模一样的,但是没有
null=True
(是否接受空值NULL,默认值False)或者blank=True
(是否接受空白内容,默认为False)等这几种参数了,有的参数是required=True/False
(请求能否为空,True不能为空,默认为True)
class RegisterForm(forms.Form):
# label 属性是form表单中特有的属性,代表这个字段的描述,这个字典类似于verbose_name
username = forms.CharField(label = u'用户名',max_length = 20,min_length=3)
# 存储到数据库的密码,是一个加密后的字符串,但是这里是通过前端传输过来的,并没进行加密
password = forms.CharField(label= u'密码',max_length=20,min_length=8)
- 表单生成HTML元素:
# views.py
class Register(View): #注册功能
def get(self,request):
# 如果需要使用django表单渲染html页面
# 实例化该表单模型,传递给前端
form = RegisterForm()
return render(request,'register_form.html',{"form":form})
def post(self,request):
# 如果不使用django表单,需要一个一个的值取出来,并且需要自己写对应的验证
username = request.POST.get('username')
password = request.POST.get('password')
return render(request,'register_form.html',locals())
# register.html
<body>
{% if username %}
提交的post数据:
{{ username }}
{{ password }}
{% else %}
<form action = "{% url "register_form" %}" method="post">
{% csrf_token %}
{{ form }} <!--会自动识别表单属性的 -->
<input type="submit" vlaue='注册'>
</form>
{% endif %}
</body>
image.png
image.png
ps: 使用django的Form类生成的表单,不包含form和submit按钮两个标签,需要手动添加。
ps : 这个模块用得比较少,这个功能确实很鸡肋,把前端该做的事情放到后台来实现,增加了代码的耦合性也增加了服务器的压力。
- 不使用表单生成元素
- 写好对应的html元素,HTML表单元素的
name
必须和django
中的表单的name
保持一致,否则匹配不到。 - 后台post方法调用
form = RegistForm(request.POST)
- 使用
is_valid()
方法可以验证用户提交的数据是否合法, 这个方法会返回一个bool,合法返回True
, 否则返回False
form.is_valid()
-
is_bound
属性:用来表示form
是否绑定了数据,如果绑定了,则返回True
,否则返回False
。即是否为None -
cleaned_data
:这个是在is_valid()
返回True
的时候,保存用户提交上来的数据。
#models.py
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(u'用户名',max_length= 20)
password = models.CharField(max_length = 40,verbose_name = u'密码')
class Meta:
db_table = 'user'
managed = True
#forms.py
# -*- coding: utf-8 -*-
from django import forms
'''
forms.py的作用
它是专门编写你的forms配置的模型
forms.py本身命名没有要求,你可以为任意名称, 但是我们一般约定它叫forms,代表这个文件是专门处理该APP下处理表单组件的
'''
class RegisterForm(forms.Form):
# label 属性是form表单中特有的属性,代表这个字段的描述,这个字典类似于verbose_name
username = forms.CharField(label = u'用户名',max_length = 20,min_length=3)
# 存储到数据库的密码,是一个加密后的字符串,但是这里是通过前端传输过来的,并没进行加密
password1 = forms.CharField(label= u'密码',max_length=20,min_length=8)
password2 = forms.CharField(label= u'密码',max_length=20,min_length=8)
#views.py
class Register(View): #注册功能
def get(self,request):
return render(request,'register_form.html')
def post(self,request):
# 如果使用表单
form = RegisterForm(request.POST)
# is_bound 是一个属性,它只验证数据字段存在不存在,不验证你的数据是否正确
print(form.is_bound)
# is_valid,这是一个方法,它会进行所有的验证,包括是否存在,跟数据是否正确
if form.is_valid():
# 使用cleaned_data 必须执行完is_valid 且返回为True才能获取数据,保存用户提交上来的数据
username = form.cleaned_data['username']
password1 = form.cleaned_data['password']
password1 = md5_password(password1)
User.objects.create(username=username,password=password1)
return HttpResponse('注册成功')
else:
return HttpResponse('数据验证失败')
#register_form.html
<body>
{% if username %}
提交的post数据:
{{ username }}
{{ password }}
{% else %}
<form action = "{% url "register_form" %}" method="post">
{% csrf_token %}
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password1"/><br/>
确认密码:<input type="password" name="password2"/><br/>
<input type="submit" vlaue='注册'>
</form>
{% endif %}
</body>
- 上传文件:
在相应的模型里面定义FileField
或者是ImageField
类型的字段,并且设置好upload_to
参数来指定上传的路径。
headshot = models.ImageField(u'头像', upload_to="upload/%Y/%m/%d")
- 如果是使用ImageField,会需要安装一个依赖Pillow, Pillow是专门做图片处理的一个python包
pip install Pillow
- 需要在
settings.py
文件中指定媒体路径MEDIA_ROOT
。
MEDIA_ROOT = "media/"
ps: 这里是媒体文件,它也是一种静态文件,在django中,这一块的内容是要跟其它静态文件分开处理的
- django 中的文件存储分为两种:
静态文件存储,一般是我们的JS、css、系统的图片文件等。
媒体文件存储,一般是用户上传的图片、文件数据,或大的文件或视频等等。 - 文件上传需要在HTML代码中的form表单中添加
enctype="multipart/form-data"
以及在views当中,使用request.FILES
来接收文件.
form = LoginUserForm(request.POST, request.FILES)
- 文件只有在保存时才会处理,数据库保存的是文件的路径,不会保存文件本身。
user = LoginUser(**form.clean())
user.save()
- 表单错误消息:
- 表单验证没有通过后,表单会产生一个
errors
属性,这个属性包括所有的验证错误信息。 - 通过
form.errors
即可访问。 - 通过
form['属性名'].erros
访问对应的错误 - 通过
form.errors.get_json_data()
或form['属性名'].erros.get_json_data()
可以将错误消息转换成json数据。 - 自定义错误消息:在
Field
中添加一个error_messages
的dict
类型的参数,然后根据属性名
设置对应的message
,例如以下代码:
password = forms.CharField(max_length=10,error_messages={'required':u'密码不能少'})
- 必须要执行完is_valid函数,否则errors是不会包含错误
- 表单自定义错误消息:
系统自带的表单数据错误信息有时无法满足我们的需求,比如用户是否已经存在了,可以在表单类中自定义错误信息,在表单中,重写方法clean_field
(field是一个属性名),可以自定义针对某一个field
的验证机制,如果出现错误
1)如果某个field
出现验证错误,通过add_error
方法给指定的field添加错误消息。
2)直接抛出一个raise ValidationError(message, code="属性名")
就可以了。
3)重写clean
方法会在先完成django
默认的验证后,再重新执行clean
方法的验证。
4)如果验证完成成功了,则直接返回当前值。
def clean_password(self):
password = self.cleaned_data.get('password',None)
if len(password) < 6:
raise forms.ValidationError(u'password at least 6 length',code='min_length')
return password
注意:
1)如果没有设置表单属性,则不会出现相应错误:
#如果不设置max_length = 20,min_length=3等属性,则不会出现错误;
password1 = forms.CharField(label= u'密码')
2)设置相关属性:
password2 = forms.CharField(label= u'密码',max_length=20,min_length=8)
image.png
3)通过error_messages字典来设置对应message
username = forms.CharField(label = u'用户名',required= True,max_length = 20,min_length=3,error_messages={'required':u'用户名不能为空','max_length':u'最大长度不能超过20个字符','min_length':u'至少3个字符'})
image.png
4)自定义错误
def clean_username(self):
username = self.cleaned_data['username']
user = User.objects.filter(username=username)
if user:
# 如果这里判断有多个错误存在,则使用add_error方法
self.add_error('username',u'用户名已存在')
# 如果只是单个错误,使用raise ValidationError,否则这里raise抛出去了,后面就不能执行了
# raise forms.ValidationError(u'用户名已存在')
#敏感字符
if username.find('mmp') >= 0:
self.add_error('username',u'存在敏感字符')
return username #刚才这里没有返回值 你对username处理完了就没有了没有了 后面的时候views.py、没有数据传进去
不管那种必须要执行完is_valid函数,否则执行相关errors是不会包含错误,form类的运行顺序是init,clean,validte,save;
如果遇到类似错误,比如说不能为空,最大最小长度时,在error_messages写了错误信息,也自定义了表单错误信息,则required=True时调用error_message,否则自定义的。
完整参考代码:
app应用urls.py
url(r'^register/$', views.Register.as_view(), name="register_form")
models.py:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(u'用户名',max_length= 10)
password = models.CharField(max_length = 40,verbose_name = u'密码')
#upload_to的设置会自动在settings配置的media文件夹下创建这样一个文件夹
#% Y / % m / % d‘被strftime()所格式化;
# ’ % Y’ 将会被格式化为一个四位数的年份, ‘ % m’ 被格式化为一个两位数的月份’ % d’是两位数日份。
headshot = models.ImageField(u'头像',upload_to='upload/%Y/%m/%d',null= True)
class Meta:
db_table = 'user'
managed = True
表单forms.py:
# -*- coding: utf-8 -*-
from django import forms
from .models import User
'''
forms.py的作用
它是专门编写你的forms配置的模型
forms.py本身命名没有要求,你可以为任意名称, 但是我们一般约定它叫forms,代表这个文件是专门处理该APP下处理表单组件的
'''
class RegisterForm(forms.Form):
# label 属性是form表单中特有的属性,代表这个字段的描述,这个字典类似于verbose_name
#根据属性名设置对应的message
username = forms.CharField(label = u'用户名',required= True,min_length = 3,max_length = 10,error_messages={'required':u'用户名不能为空','max_length':u'最大长度不能超过20个字符','min_length':u'至少3个字符'})
# 存储到数据库的密码,是一个加密后的字符串,但是这里是通过前端传输过来的,并没进行加密
password1 = forms.CharField(label= u'密码',min_length = 8,max_length = 16)
#如果没有进行
password2 = forms.CharField(label= u'密码',min_length = 8,max_length = 16)
#如果设置required= False则该字段可不填
headshot = forms.ImageField(label=u'头像',required= False)
#表单自定义错误消息:重写方法clean_field(field是一个属性名),可以自定义针对某一个field的验证机制,一个属性一个对应方法
def clean_username(self):
username = self.cleaned_data['username']
user = User.objects.filter(username=username)
if user:
# 如果这里判断有多个错误存在,则使用add_error方法
self.add_error('username',u'用户名已存在')
# 如果只是单个错误,使用raise ValidationError,否则这里raise抛出去了,后面就不能执行了
# raise forms.ValidationError(u'用户名已存在')
#敏感字符
if username.find('mmp') >= 0:
self.add_error('username',u'存在敏感字符')
return username #刚才这里没有返回值 你对username处理完了就没有了没有了 后面的时候views.py、没有数据传进去
视图views.py:
class Register(View): #注册功能
def get(self,request):
return render(request,'register_form.html')
def post(self,request):
#因为我们的文件是通过request.FILES传递的,所以如果需要上传文件,则需要把request.FILES传递进去
# form = RegisterForm(request.POST)
form = RegisterForm(request.POST,request.FILES)
# is_bound 是一个属性,它只验证数据字段存在不存在,不验证你的数据是否正确
print(form.is_bound)
# is_valid,这是一个方法,它会进行所有的验证,包括是否存在,跟数据是否正确
if form.is_valid():
# 使用cleaned_data 必须执行完is_valid 且返回为True才能获取数据,保存用户提交上来的数据
username = form.cleaned_data['username']
password1 = form.cleaned_data['password1']
password1 = md5_password(password1)
# User.objects.create(username=username,password=password1,headshot=form.cleaned_data['headshot'])
#利用字典解包方式
params = {'username':username,'password':password1,'headshot' : form.cleaned_data['headshot']}
User.objects.create(**params)
#如果使用实例化对象方式,一定要记住save保存到数据库中去;
# user = User(username = username,password=password1,headshot=form.cleaned_data['headshot'])
# user.save()
return HttpResponse('注册成功')
else:
# print(form.errors)
# print(form['username'].errors)
# print(form['username'].errors.as_json())
# print(form.errors.as_json())
return render(request,'register_form.html',locals())
html页面代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>register注册表单页面</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
{% if username %}
提交的post数据:
{{ username }}
{{ password }}
{% else %}
<p>用户注册</p>
{{ form.errors }}
<!--如果表单需要上传文件,需要设置enctype="multipart/form-data" 否则文件无法找到 -->
{#{% url 'register_form' %} 通过name找到urls配置的对应视图#}
<form action = "{% url 'register_form' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password1"/><br/>
确认密码:<input type="password" name="password2"/><br/>
上传头像:<input type="file" name = "headshot" /><br/>
<input type="submit" value="注册"/>
</form>
{% endif %}
</body>
</html>
学以致用
需求背景:
使用表单写一个用户注册
1)注册需要有头像、用户名、密码(密码存储是加密的)、性别、出生年月
2)需要做数据验证,字符长度, 是否允许为空等判断
3)使用自定义异常验证用户名是否存在
4)使用自定义异常验证两次密码是否相同
- 分析
- 需要数据库创建一个用户表,包含用户名,密码,性别,出生年月,头像字段。--在models.py中需要定义一个用户模型类;
- 关于数据验证,对于字符长度,是否允许为空,可以用表单自带的错误消息form.errors前端访问,或者用erros_messages在属性里面设置;
- 使用自定义异常验证则用在表单中,重写方法clean_field(field是一个属性名),理清form执行顺序。
模型models.py:
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(u'用户名',max_length= 10)
password = models.CharField(max_length = 40,verbose_name = u'密码') #存储数据库是用的md5加密后的所以设置长点;
sex = models.CharField(u'性别',max_length = 4,default='boy',null=True)
birthday = models.DateField(u'出生日期',auto_now_add=True)
headshot = models.ImageField(u'头像',upload_to='upload/%Y/%m/%d',null= True)
class Meta:
db_table = 'user'
managed = True
表单forms.py:
# -*- coding: utf-8 -*-
from django import forms
from .models import User
class RegisterForm(forms.Form):
username = forms.CharField(label = u'用户名',required= True,min_length = 3,max_length = 12,error_messages={'required':u'用户名不能为空','max_length':u'最大长度不能超过12个字符','min_length':u'至少3个字符'})
password = forms.CharField(label= u'密码',min_length = 8,max_length = 16,error_messages={'required':u'密码不能为空','max_length':u'最大长度不能超过16个字符','min_length':u'至少8个字符'})
password2 = forms.CharField(label= u'密码',min_length = 8,max_length = 16,error_messages={'required':u'密码不能为空','max_length':u'最大长度不能超过16个字符','min_length':u'至少8个字符'})
sex = forms.CharField(label=u'性别',max_length = 4,required= False)
birthday = forms.DateField(label=u'出生日期')
headshot = forms.ImageField(required= False)
def clean_username(self):
username = self.cleaned_data['username']
user = User.objects.filter(username=username)
if user:
# self.add_error('username',u'用户名已存在')
raise forms.ValidationError(u'用户名已存在')
return username
def clean_password2(self): #不能用clean_password,因为加载这个时候,password2还没加载出来,是没有值的。
password = self.cleaned_data['password']
password2 = self.cleaned_data['password2']
if password != password2:
raise forms.ValidationError(u'两次密码不一致')
return password
页面代码:
<body>
<h2>用户注册</h2>
{{ form.errors }}<!--form.errors访问错误信息-->
<form action = "{% url 'register_form' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
确认密码:<input type="password" name="password2"/><br/>
性别:
<input type="radio" name="sex">男
<input type="radio" name="sex">女
<br/>
上传头像:<input type="file" name = "headshot" /><br/>
出生日期:<input type="text" name="birthday"/><br/>
<input type="submit" value="注册"/>
</form>
</body>
应用urls.py:
# -*- coding:utf-8 -*-
from django.conf.urls import url,include
from TestApp import views
import views
urlpatterns = [
url(r'^register/$', views.Register.as_view(), name="register_form")
]
视图views.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render,redirect,reverse
from django.views import View
from django.http import HttpResponse
import hashlib
from .models import User
import uuid
from .forms import *
# Create your views here.
def md5_password(password): #使用md5加密
return hashlib.md5(password).hexdigest()
class Register(View): #注册功能
def get(self,request):
return render(request,'register_form.html')
def post(self,request):
form = RegisterForm(request.POST,request.FILES)
# is_bound 是一个属性,它只验证数据字段存在不存在,不验证你的数据是否正确
print(form.is_bound)
# is_valid,这是一个方法,它会进行所有的验证,包括是否存在,跟数据是否正确
if form.is_valid():
# 使用cleaned_data 必须执行完is_valid 且返回为True才能获取数据,保存用户提交上来的数据
username = form.cleaned_data['username']
password = form.cleaned_data['password']
password = md5_password(password)
birthday = form.cleaned_data['birthday']
print(len(password),password,birthday)
#利用字典解包方式
params = {'username':username,'password':password,'birthday':birthday,'headshot' : form.cleaned_data['headshot']}
User.objects.create(**params)
#如果使用实例化对象方式,一定要记住save保存到数据库中去;
# user = User(username = username,password=password1,headshot=form.cleaned_data['headshot'])
# user.save()
return HttpResponse('注册成功')
else:
return render(request,'register_form.html',locals())
页面效果:
image.png
image.png
image.png
image.png
image.png
重点
注意验证流程与逻辑。
网友评论