cookie和session

1.cookie不属于http协议范围, 由于http协议无法保持状态, 但实际情况, 我们却又需要"保持状态",因此cookie就是在这样一个场景下诞生。

cookie的工作原理是:由服务器产生内容,浏览器收到请求后保存在本地;当浏览器再次访问时,浏览器会自动带上cookie,这样服务器就能通过cookie的内容来判断这个是“谁”了。

2、cookie虽然在一定程度上解决了“保持状态”的需求,但是由于cookie本身最大支持4096字节,以及cookie本身保存在客户端,可能被拦截或窃取,因此就需要有一种新的东西,它能支持更多的字节,并且他保存在服务器,有较高的安全性。这就是session。

问题来了,基于http协议的无状态特征,服务器根本就不知道访问者是“谁”。那么上述的cookie就起到桥接的作用。

我们可以给每个客户端的cookie分配一个唯一的id,这样用户在访问时,通过cookie,服务器就知道来的人是“谁”。然后我们再根据不同的cookie的id,在服务器上保存一段时间的私密资料,如“账号密码”等等。

3、总结而言:cookie弥补了http无状态的不足,让服务器知道来的人是“谁”;但是cookie以文本的形式保存在本地,自身安全性较差;所以我们就通过cookie识别不同的用户,对应的在session里保存私密的信息以及超过4096字节的文本。

4、另外,上述所说的cookie和session其实是共通性的东西,不限于语言和框架

前几节的介绍中我们已经有能力制作一个登陆页面,在验证了用户名和密码的正确性后跳转到后台的页面。但是测试后也发现,如果绕过登陆页面。直接输入后台的url地址也可以直接访问的。这个显然是不合理的。其实我们缺失的就是cookie和session配合的验证。有了这个验证过程,我们就可以实现和其他网站一样必须登录才能进入后台页面了。

先说一下这种认证的机制。每当我们使用一款浏览器访问一个登陆页面的时候,一旦我们通过了认证。服务器端就会发送一组随机唯一的字符串(假设是123abc)到浏览器端,这个被存储在浏览端的东西就叫cookie。而服务器端也会自己存储一下用户当前的状态,比如login=true,username=hahaha之类的用户信息。但是这种存储是以字典形式存储的,字典的唯一key就是刚才发给用户的唯一的cookie值。那么如果在服务器端查看session信息的话,理论上就会看到如下样子的字典

{'123abc':{'login':true,'username:hahaha'}}

因为每个cookie都是唯一的,所以我们在电脑上换个浏览器再登陆同一个网站也需要再次验证。那么为什么说我们只是理论上看到这样子的字典呢?因为处于安全性的考虑,其实对于上面那个大字典不光key值123abc是被加密的,value值{'login':true,'username:hahaha'}在服务器端也是一样被加密的。所以我们服务器上就算打开session信息看到的也是类似与以下样子的东西

{'123abc':dasdasdasd1231231da1231231}

django的session默认是存储在数据库里的

下面我们再来最后总结一下cookie和session的知识点

一、操作Cookie

  获取cookie: requst.cookie[key]

  设置cookie: response.set_cookie(key,value)

  由于cookie保存在客户端的电脑上,所以,jquery也可以操作cookie。

<script src='http://830909.blog.51cto.com/static/js/jquery.cookie.js'></script>
$.cookie("list_pager_num", 30,{ path: '/' });

二、操作Session(session默认在服务器端保存15天)

  获取session:request.session[key]

  设置session:reqeust.session[key] = value

  删除session:del request.session[key]

  (这个删除其实就是把数据库的session_data更新为一个其他的值了,并没有立即删除)

request.session.set_expiry(value)
* 如果value是个整数,session会在些秒数后失效。
* 如果value是个datatime或timedelta,session就会在这个时间后失效。
* 如果value是0,用户关闭浏览器session就会失效。
* 如果value是None,session会依赖全局session失效策略。

分页

  1.手动分页

 ************************视图函数****************
uselist = []
for i in range(1,100):
use_data ={"username":"root"+str(i),"age":i}
uselist.append(use_data) def orginal_index(request):
per_page_num = 10 #定义每页显示的记录条数
current_page = request.GET.get("p") #获取用户指定页码
try: #对用户输入的指定页码做异常处理,如果小于等于1或者类型错误都让它跳转至第一页,
current_page = int(current_page)
if current_page<=1:
current_page = 1
except Exception as e:
current_page = 1
statr_page = (current_page-1)*per_page_num #每页的开始条数下标
end_page = (current_page)*per_page_num #每页的结束条数下标
data = uselist[statr_page:end_page] #切片
pre_page = current_page-1 #上一页页码
next_page = current_page+1 #下一页页码
x,y = divmod(len(uselist),per_page_num) #对分页的总数做个判断
if y > 0:
x = x+1
if next_page>x: #如果下一页的页码大于总页码让它跳转至最后一页
next_page = x
return render(request,"orginal_index.html",{"userlist":data,"previous_page":pre_page,"next_page":next_page}) *********************html******************************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <ul>
{% for i in userlist%}
<li>{{ i.username }}-{{ i.age }}</li>
{% endfor %}
<p><a href="/orginal_index?p={{ previous_page }}">上一页</a>
<a href="/orginal_index?p={{ next_page }}">下一页</a></p>
</ul> </body>
</html>

  2.django内置分页

 *********************视图函数***********************
# 内置分页
from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage
uselist = []
for i in range(1,100):
use_data ={"username":"root"+str(i),"age":i}
uselist.append(use_data)
def inner_index(request):
current_page = request.GET.get("p")
paginator = Paginator(uselist,10) #创建Paginator对象,第一个参数为数据源,第二个为每页显示条数
# Paginator对象内置方法:
# per_page: 每页显示条目数量
# count: 数据总个数
# num_pages:总页数
# page_range:总页数的索引范围,如: (1,10),(1,200)
# page: page对象(封装了上一页,下一页方法)
try:
posts = paginator.page(current_page) #调用paginator内置page方法
#posts就是page对象,里面封装了以下方法:
# has_next 是否有下一页
# next_page_number 下一页页码
# has_previous 是否有上一页
# previous_page_number 上一页页码
# object_list 分页之后的数据列表 ,切片好的数据
# number 当前页
# paginator paginator对象
except PageNotAnInteger as e: #对用户传入的current_page不是整形进行异常处理
posts = paginator.page(1) #让其等于第一页
except EmptyPage: #如果用户传入的current_page是一个不存在的页码或者空值异常处理
posts = paginator.page(paginator.num_pages) #让其等于最后一页
return render(request,"inner_index.html",{"posts":posts}) ***********************html********************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <ul>
{% for i in posts.object_list%}
<li>{{ i.username }}-{{ i.age }}</li>
{% endfor %}
{% if posts.has_previous %}
<a href="/inner_index?p={{ posts.previous_page_number }}">上一页</a>
{% else %}
<a href="#">上一页</a>
{% endif %}
{% if posts.has_next %}
<a href="/inner_index?p={{ posts.next_page_number }}">下一页</a>
{% else %}
<a href="#">下一页</a>
{% endif %}
<span>{{ posts.number }}/{{ posts.paginator.num_pages }}</span>
</ul> </body>
</html>

  3.内置分页扩展

 ************************视图函数********************
# 内置分页拓展
from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage
uselist = []
for i in range(1,923):
use_data ={"username":"root"+str(i),"age":i}
uselist.append(use_data) class MyPaginator(Paginator): #继承
def __init__(self,current_page,per_page_num,*args,**kwargs):
super(MyPaginator,self).__init__(*args,**kwargs) #继承父类Paingarot方法
self.current_page = int(current_page) #当前页
self.per_page_num = int(per_page_num) #每一页显示的最多页码总数 def range_index(self):
if self.num_pages<self.per_page_num: #如果总页码小于每一页显示的最多页码总数
return range(1,self.num_pages+1)
part = int(self.per_page_num/2)
print(part)
print(self.current_page)
if self.current_page <= part: #如果当前页小于等于每页显示最多页码数的一半
return range(1,self.per_page_num+1)
if self.current_page+part>self.num_pages: #如果当前页加每页显示最多页码数的一半大于总页码数
return range(self.num_pages-self.per_page_num,self.num_pages+1)
return range(self.current_page-part,self.current_page+part+1)
def inner_index(request):
current_page = request.GET.get("p")
paginator = MyPaginator(current_page,11,uselist,10) #创建自定义的继承类Paingarot方法,第一个参数为用户传入的当前页码
# ,第二个参数为每一页显示页码的数量,第三个参数为数据源,第四个参数为每一页显示的条数
# Paginator对象内置方法:
# per_page: 每页显示条目数量
# count: 数据总个数
# num_pages:总页数
# page_range:总页数的索引范围,如: (1,10),(1,200)
# page: page对象(封装了上一页,下一页方法)
try:
posts = paginator.page(current_page) #调用paginator内置page方法
#posts就是page对象,里面封装了以下方法:
# has_next 是否有下一页
# next_page_number 下一页页码
# has_previous 是否有上一页
# previous_page_number 上一页页码
# object_list 分页之后的数据列表 ,切片好的数据
# number 当前页
# paginator paginator对象
except PageNotAnInteger as e: #对用户传入的current_page不是整形进行异常处理
posts = paginator.page(1) #让其等于第一页
except EmptyPage: #如果用户传入的current_page是一个不存在的页码或者空值异常处理
posts = paginator.page(paginator.num_pages) #让其等于最后一页
return render(request,"inner_index.html",{"posts":posts}) ***********************html***********************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <ul>
{% for i in posts.object_list%}
<li>{{ i.username }}-{{ i.age }}</li>
{% endfor %}
{% if posts.has_previous %}
<a href="/inner_index?p={{ posts.previous_page_number }}">上一页</a>
{% else %}
<a href="#">上一页</a>
{% endif %}
{% for v in posts.paginator.range_index %}
{% if posts.number == v %}
<a style="font-size: 60px" href="/inner_index?p={{ v }}">{{ v }}</a>
{% else %}
<a href="/inner_index?p={{ v }}">{{ v }}</a>
{% endif %}
{% endfor %}
{% if posts.has_next %}
<a href="/inner_index?p={{ posts.next_page_number }}">下一页</a>
{% else %}
<a href="#">下一页</a>
{% endif %}
<span>{{ posts.number }}/{{ posts.paginator.num_pages }}</span>
</ul> </body>
</html>

  4.自定义分页组件

  由于django分页时对数据有延迟加载,所以当数据源比较大时,django内置的分页组件仍然适用,但其他框架里不一定有延迟加载,所以当数据源比较大时,如果每次分页时都调用整个数据库的数据,显然是不合适的,所以我们可以通过切片方式自定义分页组件,适用任何框架

 ***************views.py*********************

 from app01.diy_per_page import *   #diy_per_page为我们自己单独写的分页组件文件
uselist = []
for i in range(1,923):
use_data ={"username":"root"+str(i),"age":i}
uselist.append(use_data) def diy_index(request):
current_page = request.GET.get("p")
obj = Diy_page(len(uselist),current_page,10,11)
data = uselist[obj.start_page():obj.end_page()]
return render(request,"diy_index.html",{"object_list":data,"obj":obj}) *****************diy_per_page.py******************
class Diy_page(object):
def __init__(self,total_count,current_page,per_page_num,max_page_num):
# 数据源总记录条数
self.total_count = total_count
# 接收用户发送的当前页码
try:
v = int(current_page)
if v<=0: #如果用户输入的页码小于1,直接让它等于1
v = 1
self.current_page = v
except Exception as e:
self.current_page = 1
# 每一页显示的记录条数
self.per_page_num = per_page_num
# 每一页显示的最多页码数
self.max_page_num = max_page_num
@property
def num_pages(self):
'''
总页码数
:return:
'''
a,b = divmod(self.total_count,self.per_page_num)
if b == 0:
return a
else:
return a+1
def page_range(self):
'''
每一页所能显示的最多页码数范围
:return:
'''
if self.num_pages<self.max_page_num: #如果总页码小于每一页显示的最多页码总数
return range(1,self.num_pages+1)
part = int(self.max_page_num/2)
if self.current_page <= part: #如果当前页小于等于每页显示最多页码数的一半
return range(1,self.max_page_num+1)
if self.current_page+part>self.num_pages: #如果当前页加每页显示最多页码数的一半大于总页码数
return range(self.num_pages-self.max_page_num,self.num_pages+1)
return range(self.current_page-part,self.current_page+part+1)
def start_page(self):
'''
切片的起始位置
:return:
'''
return (self.current_page-1)*self.per_page_num
def end_page(self):
'''
切片的结束位置
:return:
'''
return self.current_page*self.per_page_num
def a_num_pages(self):
'''
每一页底部页码链接标签
:return:
'''
li = []
head_page = "<li><a href='/diy_index?p=1'>首页</a></li>"
li.append(head_page)
if self.current_page ==1:
prev_page = "<li><a href='#'>上一页</a></li>" #生成上一页标签
else:
prev_page = "<li><a href='/diy_index?p=%s'>上一页</a></li>"%(self.current_page-1)
li.append(prev_page)
for row in self.page_range():
if row == self.current_page:
data = "<li class='active'><a href='/diy_index?p=%s'>%s</a></li>"%(row,row)
else:
data = "<li><a href='/diy_index?p=%s'>%s</a></li>" % (row, row)
li.append(data)
if self.current_page ==self.num_pages:
next_page = "<li><a href='#'>下一页</a></li>" #生成下一页标签
else:
next_page = "<li><a href='/diy_index?p=%s'>下一页</a><li>"%(self.current_page+1)
li.append(next_page)
last_page = "<li><a href='/diy_index?p=%s'>尾页</a><li>"%self.num_pages
li.append(last_page)
return "".join(li) #返回前端是拼接的字符串 ***************html**************************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"> #为了格式好看,我们引入bootstrap文件,调用其分页插件,相应标签属性对应修改,在此不加详细阐述
</head>
<body> <ul>
{% for i in object_list%}
<li>{{ i.username }}-{{ i.age }}</li>
{% endfor %} <ul class="pagination">
{{ obj.a_num_pages|safe }}
</ul>
</ul> </body>
</html>

Form组件

功能:(1)对用户请求的验证(显示错误信息)

        注意:由于浏览器默认也会对用户提交的数据做验证,所以在html表单里添加novalidate属性,即可取消浏览器端的验证

           对于form表单,在通过ajax发送请求的时候,获取表单内容可以通过$(表单).serialize()获取该表单内容,发送至后端仍是键值对格式

   (2)生成html代码

   (3)HTML Form提交保留上次提交数据

   (4)初始化页面显示内容

创建基本form

 ********************form类****************

 from django.shortcuts import render,HttpResponse,redirect
from django import forms
from django.forms import fields
from app02 import models
# Create your views here. class Myform(forms.Form): #定义form类
username = fields.CharField(
required=True,
max_length=32,
min_length=6,
label="用户名",
initial="请输入用户名",
error_messages={
"required":"不能为空", #用定义的属性字段来定义错误信息
"invalid":"格式错误" #格式错误都用invalid定义
} )
passwd = fields.IntegerField(
required=True,
label="密码"
)
e_mail = fields.EmailField(
required=True,
label="邮箱" ) ****************views处理函数*************
def myform1(request):
user_list = models.User_info.objects.all()
return render(request,"my_form1.html",{"user_list":user_list}) def add_user(request):
if request.method == "GET":
obj = Myform() #此处实例化form对象,对象里的字段可以通过静态方法调用,且在前端调用时会执行内部的__str__()方法,生成对应的标签
return render(request,"add_user.html",{"obj":obj})
else:
obj = Myform(request.POST) #此处会将前端接收的数据传递给form类,此时并不会进行验证
if obj.is_valid(): #此处进行验证
models.User_info.objects.create(**obj.cleaned_data) #cleaned_data为验证过的用户数据,为字典格式
return redirect("/my_form1")
else:
return render(request,"add_user.html",{"obj":obj}) def edit_user(request):
nid = request.GET.get("p")
if request.method == "GET":
obj = models.User_info.objects.filter(id = nid).first()
data = Myform({"username":obj.username,"passwd":obj.passwd,"e_mail":obj.e_mail}) #实例化时传入自定义字典可以在前端设置默认值,字段要跟form对应上
return render(request,"edit_user.html",{"data":data,"nid":nid}) else:
data = Myform(request.POST)
if data.is_valid():
models.User_info.objects.filter(id = nid).update(**data.cleaned_data)
return redirect("/my_form1")
else:
return render(request,"edit_user.html",{"data":data,"nid":nid}) ********************html*************************

View

 **************my_form1****************
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="/add_user">添加</a>
<ul>
{% for row in user_list %}
<li>{{ row.username }}-{{ row.passwd }}-{{ row.e_mail }} <a href="/edit_user?p={{ row.id }}">修改</a></li>
{% endfor %}
</ul> </body>
</html> ****************edit_user*******************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body> <form action="/edit_user?p={{ nid }}" method="post" novalidate>
{% csrf_token %}
<p>{{ data.username.label }}{{ data.username }}{{ data.username.errors.0 }}</p>
<p>{{ data.passwd.label }}{{ data.passwd }}{{ data.passwd.errors.0 }}</p>
<p>{{ data.e_mail.label }}{{ data.e_mail }}{{ data.e_mail.errors.0 }}</p>
<p><input type="submit"></p>
</form> </body>
</html> ***************add_user*********************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/add_user" method="post" novalidate>
{% csrf_token %}
<p>{{ obj.username.label }}{{ obj.username }}{{ obj.username.errors.0 }}</p>
<p>{{ obj.passwd.label }}{{ obj.passwd }}{{ obj.passwd.errors.0 }}</p>
<p>{{ obj.e_mail.label }}{{ obj.e_mail }}{{ obj.e_mail.errors.0 }}</p>
<p><input type="submit"></p>
</form> </body>
</html> html

HTML

  FORM类

创建Form类时,主要涉及到字段和插件,字段用于对用户请求数据的验证,插件用于自动生成HTML

1, django内置字段:

 Field
required=True, 是否允许为空
widget=None, HTML插件
label=None, 用于生成Label标签或显示内容
initial=None, 初始值
help_text='', 帮助信息(在标签旁边显示)
error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'}
show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[], 自定义验证规则
localize=False, 是否支持本地化
disabled=False, 是否可以编辑
label_suffix=None Label内容后缀 CharField(Field)
max_length=None, 最大长度
min_length=None, 最小长度
strip=True 是否移除用户输入空白 IntegerField(Field)
max_value=None, 最大值
min_value=None, 最小值 FloatField(IntegerField)
... DecimalField(IntegerField)
max_value=None, 最大值
min_value=None, 最小值
max_digits=None, 总长度
decimal_places=None, 小数位长度 BaseTemporalField(Field)
input_formats=None 时间格式化 DateField(BaseTemporalField) 格式:2015-09-01
TimeField(BaseTemporalField) 格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 DurationField(Field) 时间间隔:%d %H:%M:%S.%f
... RegexField(CharField)
regex, 自定制正则表达式
max_length=None, 最大长度
min_length=None, 最小长度
error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} EmailField(CharField)
... FileField(Field)
allow_empty_file=False 是否允许空文件 ImageField(FileField)
...
注:需要PIL模块,pip3 install Pillow
以上两个字典使用时,需要注意两点:
- form表单中 enctype="multipart/form-data"
- view函数中 obj = MyForm(request.POST, request.FILES) URLField(Field)
... BooleanField(Field)
... NullBooleanField(BooleanField)
... ChoiceField(Field)
...
choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),)
required=True, 是否必填
widget=None, 插件,默认select插件
label=None, Label内容
initial=None, 初始值
help_text='', 帮助提示 ModelChoiceField(ChoiceField)
... django.forms.models.ModelChoiceField
queryset, # 查询数据库中的数据
empty_label="---------", # 默认空显示内容
to_field_name=None, # HTML中value的值对应的字段
limit_choices_to=None # ModelForm中对queryset二次筛选 ModelMultipleChoiceField(ModelChoiceField)
... django.forms.models.ModelMultipleChoiceField TypedChoiceField(ChoiceField)
coerce = lambda val: val 对选中的值进行一次转换
empty_value= '' 空值的默认值 MultipleChoiceField(ChoiceField)
... TypedMultipleChoiceField(MultipleChoiceField)
coerce = lambda val: val 对选中的每一个值进行一次转换
empty_value= '' 空值的默认值 ComboField(Field)
fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式
fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) MultiValueField(Field)
PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 SplitDateTimeField(MultiValueField)
input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
required=True,
widget=None,
label=None,
initial=None,
help_text='' GenericIPAddressField
protocol='both', both,ipv4,ipv6支持的IP格式
unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 SlugField(CharField) 数字,字母,下划线,减号(连字符)
... UUIDField(CharField) uuid类型
...

注:UUID是个根据MAC以及当前时间等创建的不重复的随机字符串

2, django内置插件

 TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget  

常用选择插件

 # 单radio,值为字符串
# user = fields.CharField(
# initial=2,
# widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# ) # 单radio,值为字符串
# user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
# initial=2,
# widget=widgets.RadioSelect
# ) # 单select,值为字符串
# user = fields.CharField(
# initial=2,
# widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# ) # 单select,值为字符串
# user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
# initial=2,
# widget=widgets.Select
# ) # 多选select,值为列表
# user = fields.MultipleChoiceField(
# choices=((1,'上海'),(2,'北京'),),
# initial=[1,],
# widget=widgets.SelectMultiple
# ) # 单checkbox
# user = fields.CharField(
# widget=widgets.CheckboxInput()
# ) # 多选checkbox,值为列表
# user = fields.MultipleChoiceField(
# initial=[2, ],
# choices=((1, '上海'), (2, '北京'),),
# widget=widgets.CheckboxSelectMultiple
# )

  在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。

 from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator class MyForm(Form): user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
initial=2,
widget=widgets.Select
) def __init__(self, *args, **kwargs):
super(MyForm,self).__init__(*args, **kwargs) #注意此句跟下句位置不能调换,super方法的作用相当于将form类的所有静态字段加载到内存,然后下一句fields对象才能找到对应的字段
# self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
# 或
self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')  

方式二:

使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现

 from django import forms

 from django.forms import fields
from django.forms import widgets
from django.forms import models as form_model #需要导入此模块
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator class FInfo(forms.Form):
authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())
# authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all()) #注意由于前端调用的时候是调用的字段对象方法,所以要显示对象的值需要在model对应的类中定义__str()__方法,返回类的字段  

  自定义验证规则

方法一:

 from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator class MyForm(Form):
user = fields.CharField(
validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
)

方法二:

 import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError #需导入 # 自定义验证规则
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误') class PublishForm(Form): title = fields.CharField(max_length=20,
min_length=5,
error_messages={'required': '标题不能为空',
'min_length': '标题最少为5个字符',
'max_length': '标题最多为20个字符'},
widget=widgets.TextInput(attrs={'class': "form-control",
'placeholder': '标题5-20个字符'})) # 使用自定义验证规则
phone = fields.CharField(validators=[mobile_validate, ],
error_messages={'required': '手机不能为空'},
widget=widgets.TextInput(attrs={'class': "form-control",
'placeholder': u'手机号码'})) email = fields.EmailField(required=False,
error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

方法三:自定义方法

 from django import forms
from django.forms import fields
from django.forms import widgets
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator class FInfo(forms.Form):
username = fields.CharField(max_length=5,
validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], )
email = fields.EmailField() def clean_username(self): #clean_+字段名(固定格式),由于字典无序,只能对当前自己字段进行验证,验证过程中不能涉及其他字段
"""
Form中字段中定义的格式匹配完之后,执行此方法进行验证
:return:
"""
value = self.cleaned_data['username'] #从字段与对应的值组成的字典中取出对应的值
if "" in value:
raise ValidationError('666已经被玩烂了...', 'invalid') #引发特定异常,第一个参数为错误信息提示,第二个为错误类型,不定义默认用invalid
return value #必须有返回值,如果验证通过,字典中对应的该字段相当于重新进行了赋值,但值没改

方法四:自定义整体验证

 from django.core.exceptions import ValidationError
class Form_diy(forms.Form):
username = fields.CharField(max_length=32)
user_id = fields.IntegerField()
def clean(self): #clean名字固定,此时所有字段已执行完单独的验证
value_dict = self.cleaned_data #包含所有的用户提交过来的信息
v1 = value_dict["username"]
v2 = value_dict["user_id"]
if v1 == "eric" and v2 == 12: #自定义验证
raise ValidationError("整体信息错误") #抛出异常
'''
注意:此处抛出的错误信息会被放进errors字典里面,且键为__all__
errors错误信息的字典格式为
{
__all__:["整体错误信息",],
字段一:[mes1,mes2,],
字段二:[mes1,mes2,],
}
'''
return value_dict

方法五:同时生成多个标签验证

 from django.forms import Form
from django.forms import widgets
from django.forms import fields from django.core.validators import RegexValidator ############## 自定义字段 ##############
class PhoneField(fields.MultiValueField):
def __init__(self, *args, **kwargs):
# Define one message for all fields.
error_messages = {
'incomplete': 'Enter a country calling code and a phone number.',
}
# Or define a different message for each field.
f = (
fields.CharField(
error_messages={'incomplete': 'Enter a country calling code.'},
validators=[
RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'),
],
),
fields.CharField(
error_messages={'incomplete': 'Enter a phone number.'},
validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')],
),
fields.CharField(
validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')],
required=False,
),
)
super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args,
**kwargs) def compress(self, data_list):
"""
当用户验证都通过后,该值返回给用户
:param data_list:
:return:
"""
return data_list ############## 自定义插件 ##############
class SplitPhoneWidget(widgets.MultiWidget):
def __init__(self):
ws = (
widgets.TextInput(),
widgets.TextInput(),
widgets.TextInput(),
)
super(SplitPhoneWidget, self).__init__(ws) def decompress(self, value):
"""
处理初始值,当初始值initial不是列表时,调用该方法
:param value:
:return:
"""
if value:
return value.split(',')
return [None, None, None]

序列化

  简单来说,序列化就是把某种东西转化成能够可以保存在文件里的过程,叫做序列化,反之即反序列化

  json只能序列化python基本的数据类型

  通过ajax发送数据的时候,如果数据里面有列表,加入traditional:True,防止jquery会深度序列化参数对象

  对于django特有的queryset对象。需要用serializers.serializers("json",queryset对象)将其序列化

最新文章

  1. 创建第一个 local network(II)- 每天5分钟玩转 OpenStack(81)
  2. CSS3透明属性opacity
  3. Oracle数据库坏块的恢复
  4. MyEclipse 优化
  5. C# 常用类
  6. gitlab配置邮件通知
  7. node.js的学习
  8. HDOJ 1220 Cube
  9. linux 内核调试方法
  10. 特征提取(Detect)、特征描述(Descriptor)、特征匹配(Match)的通俗解释
  11. 【NET】Winform用户控件的初步封装之编辑控件
  12. IP、端口及远程服务器
  13. asp.net IHttpHandler浅析
  14. BZOJ5207 : [Jsoi2017]隧道
  15. 如何手动删除服务?Windows手动删除服务列表中残留服务的方法
  16. [QGLViewer]鼠标取点后回调
  17. Struts2将图片输出到页面
  18. 界面编程之QT绘图和绘图设备20180728
  19. 使用Aptana Studio 3开发让Extjs变的更简单
  20. MySQL 5.6 GTID 原理以及使用

热门文章

  1. 从零学React Native之08Image组件
  2. Android Tween和Frame 动画
  3. 错误处理——According to TLD or attribute directive in tag file, attribute test does not accept any expres
  4. 基于Mysql实现分布式锁
  5. ELMo解读(论文 + PyTorch源码)
  6. ThinkPHP5.1接收post、get参数
  7. Python--day70--csrf简单用法、 跨站请求伪造和csrf_token使用
  8. 深入理解 Embedding层的本质
  9. 通俗理解tf.name_scope()、tf.variable_scope()
  10. CodeForces 825G&quot;Tree Queries&quot;(选根建树)