django框架(2)
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对象)将其序列化
django框架(2)的更多相关文章
- MVC其实很简单(Django框架)
Django框架MVC其实很简单 让我们来研究一个简单的例子,通过该实例,你可以分辨出,通过Web框架来实现的功能与之前的方式有何不同. 下面就是通过使用Django来完成以上功能的例子: 首先,我们 ...
- Django框架-目录文件简介
Rhel6.5 Django1.10 Python3.5 Django框架-目录文件简介 1.介绍Django Django:一个可以使Web开发工作愉快并且高效的Web开发框架. 使用Django, ...
- Django框架学习
两个月前学习的Django框架,写了个简易婚恋调查网站,代码就懒得全贴了,有两张图记录下
- django框架的models
在django的框架设计中采用了mtv模型,即Model,template,viewer Model相对于传统的三层或者mvc框架来说就相当对数据处理层,它主要负责与数据的交互,在使用django框架 ...
- Windows上python开发--2安装django框架
Windows上python开发--2安装django框架 分类: 服务器后台开发2014-05-17 21:22 2310人阅读 评论(2) 收藏 举报 python django 上一篇文章中讲了 ...
- MySQL在Django框架下的基本操作(MySQL在Linux下配置)
[原]本文根据实际操作主要介绍了Django框架下MySQL的一些常用操作,核心内容如下: ------------------------------------------------------ ...
- django框架介绍
主要内容 1. Django框架发展 2. Django架构,MTV模式 3. 开发流程 4. 开发实例——Poll python下各种框架 一 ...
- Django学习(二) Django框架简单搭建
为了快速学习Python进行Web的开发,所以我不准备从Python的基础学起,直接从Django框架入手,边学框架边学Python的基础知识. 下面就开始Django的快速开发之旅吧. 关于Djan ...
- Django - Django框架 简单介绍
Django框架 简单介绍 本文地址: http://blog.csdn.net/caroline_wendy/article/details/29172271 1. 介绍 Django是一个开放源码 ...
- Django框架全面讲解
Python的WEB框架有Django.Tornado.Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM.模型绑定.模板引擎.缓存.Session等诸多功能. ...
随机推荐
- Java练习 SDUT-2246_时间日期格式转换
时间日期格式转换 Time Limit: 1000 ms Memory Limit: 65536 KiB Problem Description 对于日期的常用格式,在中国常采用格式的是"年 ...
- 17.使用android_studio开发libgdx
以前用eclipse开发libgdx,由于每次开机都会自检一遍安卓环境,觉得慢,就把安卓项目包给关掉了,结果再打开资源目录发生了变化,导致安卓打包不了,所以决定尝试使用as开发 首先安装as,导入gd ...
- Hibernate错误——No row with the given identifier exists
错误 是用的是Hibernate自动建立的数据表,在进行数据库操作时,出现错误No row with the given identifier exists 解决 关系数据库一致性遭到了破坏,找到相关 ...
- qt开发ROS gui界面环境配置过程总结
这段时间花了点时间配置了在qtcreator5.9.1上开发ros gui界面的环境,终于可以实现导入工程,插断点调试了.总结起来需要注意以下几点: 1.安装插件ros_qtc_plugin,ROS与 ...
- vue element 给指数的div加loading
const loading = this.$loading({ lock: true, text: 'Loading', spinner: 'el-icon-lo ...
- HZOJ 数颜色
一眼看去树套树啊,我可能是数据结构学傻了…… 是应该去学一下莫队进阶的东西了. 上面那个东西我没有打,所以这里没有代码,而且应该也不难理解吧. 这么多平衡树就算了,不过线段树还是挺好打的. 正解3: ...
- mysql数据库之存储引擎
mysql存储引擎概述 什么是存储引擎? MYSQL中的数据用各不相同的技术 ...
- Pytorch: 命名实体识别: BertForTokenClassification/pytorch-crf
文章目录基本介绍BertForTokenClassificationpytorch-crf实验项目参考基本介绍命名实体识别:命名实体识别任务是NLP中的一个基础任务.主要是从一句话中识别出命名实体.比 ...
- oracle 基础表的选择
基础表(Driving Table)是指被最先访问的表(通常以全表扫描的方式被访问). 根据优化器的不同, SQL语句中基础表的选择是不一样的. 如果你使用的是CBO (COST BASED OPTI ...
- Python--day19--collections模块
常用模块一的各个模块解释: 文件名不要起跟模块名一样:(模块本身就是一个py文件) collection模块: namedtuple方法: 例1: 例2: dequeue方法:双端队列 有序字典Ord ...