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.手动分页

 1 ************************视图函数****************
 2 uselist = []
 3 for i in range(1,100):
 4     use_data ={"username":"root"+str(i),"age":i}
 5     uselist.append(use_data)
 6 
 7 def orginal_index(request):
 8     per_page_num = 10   #定义每页显示的记录条数
 9     current_page = request.GET.get("p")  #获取用户指定页码
10     try:             #对用户输入的指定页码做异常处理,如果小于等于1或者类型错误都让它跳转至第一页,
11         current_page = int(current_page)
12         if current_page<=1:
13             current_page = 1
14     except Exception as e:
15         current_page = 1
16     statr_page = (current_page-1)*per_page_num   #每页的开始条数下标
17     end_page = (current_page)*per_page_num      #每页的结束条数下标
18     data = uselist[statr_page:end_page]       #切片
19     pre_page = current_page-1  #上一页页码
20     next_page = current_page+1    #下一页页码
21     x,y = divmod(len(uselist),per_page_num)  #对分页的总数做个判断
22     if y > 0:
23         x = x+1
24         if next_page>x:   #如果下一页的页码大于总页码让它跳转至最后一页
25             next_page = x
26     return render(request,"orginal_index.html",{"userlist":data,"previous_page":pre_page,"next_page":next_page})
27 
28 
29 
30 *********************html******************************
31 <!DOCTYPE html>
32 <html lang="en">
33 <head>
34     <meta charset="UTF-8">
35     <title>Title</title>
36 </head>
37 <body>
38 
39 <ul>
40     {% for i in userlist%}
41         <li>{{ i.username }}-{{ i.age }}</li>
42     {% endfor %}
43     <p><a href="/orginal_index?p={{ previous_page }}">上一页</a>
44         <a href="/orginal_index?p={{ next_page }}">下一页</a></p>
45 </ul>
46 
47 </body>
48 </html>
View Code

  2.django内置分页

 1 *********************视图函数***********************
 2 # 内置分页
 3 from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage
 4 uselist = []
 5 for i in range(1,100):
 6     use_data ={"username":"root"+str(i),"age":i}
 7     uselist.append(use_data)
 8 def inner_index(request):
 9     current_page = request.GET.get("p")
10     paginator = Paginator(uselist,10) #创建Paginator对象,第一个参数为数据源,第二个为每页显示条数
11     # Paginator对象内置方法:
12         # per_page: 每页显示条目数量
13         # count:    数据总个数
14         # num_pages:总页数
15         # page_range:总页数的索引范围,如: (1,10),(1,200)
16         # page:     page对象(封装了上一页,下一页方法)
17     try:
18         posts = paginator.page(current_page) #调用paginator内置page方法
19         #posts就是page对象,里面封装了以下方法:
20         # has_next              是否有下一页
21         # next_page_number      下一页页码
22         # has_previous          是否有上一页
23         # previous_page_number  上一页页码
24         # object_list           分页之后的数据列表 ,切片好的数据
25         # number                当前页
26         # paginator             paginator对象
27     except PageNotAnInteger as e:  #对用户传入的current_page不是整形进行异常处理
28         posts = paginator.page(1) #让其等于第一页
29     except EmptyPage:  #如果用户传入的current_page是一个不存在的页码或者空值异常处理
30         posts = paginator.page(paginator.num_pages)   #让其等于最后一页
31     return render(request,"inner_index.html",{"posts":posts})
32 
33 
34 
35 ***********************html********************
36 <!DOCTYPE html>
37 <html lang="en">
38 <head>
39     <meta charset="UTF-8">
40     <title>Title</title>
41 </head>
42 <body>
43 
44 <ul>
45     {% for i in posts.object_list%}
46         <li>{{ i.username }}-{{ i.age }}</li>
47     {% endfor %}
48     {% if posts.has_previous %}
49         <a href="/inner_index?p={{ posts.previous_page_number }}">上一页</a>
50     {% else %}
51         <a href="#">上一页</a>
52     {% endif %}
53     {% if posts.has_next %}
54         <a href="/inner_index?p={{ posts.next_page_number }}">下一页</a>
55     {% else %}
56         <a href="#">下一页</a>
57     {% endif %}
58     <span>{{ posts.number }}/{{ posts.paginator.num_pages }}</span>
59 </ul>
60 
61 </body>
62 </html>
View Code

  3.内置分页扩展

 1 ************************视图函数********************
 2 # 内置分页拓展
 3 from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage
 4 uselist = []
 5 for i in range(1,923):
 6     use_data ={"username":"root"+str(i),"age":i}
 7     uselist.append(use_data)
 8 
 9 class MyPaginator(Paginator):  #继承
10     def __init__(self,current_page,per_page_num,*args,**kwargs):
11         super(MyPaginator,self).__init__(*args,**kwargs)   #继承父类Paingarot方法
12         self.current_page = int(current_page)  #当前页
13         self.per_page_num = int(per_page_num)   #每一页显示的最多页码总数
14 
15     def range_index(self):
16         if self.num_pages<self.per_page_num:  #如果总页码小于每一页显示的最多页码总数
17             return range(1,self.num_pages+1)
18         part = int(self.per_page_num/2)
19         print(part)
20         print(self.current_page)
21         if self.current_page <= part:  #如果当前页小于等于每页显示最多页码数的一半
22             return range(1,self.per_page_num+1)
23         if self.current_page+part>self.num_pages:   #如果当前页加每页显示最多页码数的一半大于总页码数
24             return range(self.num_pages-self.per_page_num,self.num_pages+1)
25         return range(self.current_page-part,self.current_page+part+1)
26 def inner_index(request):
27     current_page = request.GET.get("p")
28     paginator = MyPaginator(current_page,11,uselist,10) #创建自定义的继承类Paingarot方法,第一个参数为用户传入的当前页码
29                 # ,第二个参数为每一页显示页码的数量,第三个参数为数据源,第四个参数为每一页显示的条数
30     # Paginator对象内置方法:
31         # per_page: 每页显示条目数量
32         # count:    数据总个数
33         # num_pages:总页数
34         # page_range:总页数的索引范围,如: (1,10),(1,200)
35         # page:     page对象(封装了上一页,下一页方法)
36     try:
37         posts = paginator.page(current_page) #调用paginator内置page方法
38         #posts就是page对象,里面封装了以下方法:
39         # has_next              是否有下一页
40         # next_page_number      下一页页码
41         # has_previous          是否有上一页
42         # previous_page_number  上一页页码
43         # object_list           分页之后的数据列表 ,切片好的数据
44         # number                当前页
45         # paginator             paginator对象
46     except PageNotAnInteger as e:  #对用户传入的current_page不是整形进行异常处理
47         posts = paginator.page(1) #让其等于第一页
48     except EmptyPage:  #如果用户传入的current_page是一个不存在的页码或者空值异常处理
49         posts = paginator.page(paginator.num_pages)   #让其等于最后一页
50     return render(request,"inner_index.html",{"posts":posts})
51 
52 
53 ***********************html***********************
54 <!DOCTYPE html>
55 <html lang="en">
56 <head>
57     <meta charset="UTF-8">
58     <title>Title</title>
59 </head>
60 <body>
61 
62 <ul>
63     {% for i in posts.object_list%}
64         <li>{{ i.username }}-{{ i.age }}</li>
65     {% endfor %}
66     {% if posts.has_previous %}
67         <a href="/inner_index?p={{ posts.previous_page_number }}">上一页</a>
68     {% else %}
69         <a href="#">上一页</a>
70     {% endif %}
71     {% for v in posts.paginator.range_index %}
72         {% if posts.number == v %}
73             <a style="font-size: 60px" href="/inner_index?p={{ v }}">{{ v }}</a>
74         {% else %}
75             <a href="/inner_index?p={{ v }}">{{ v }}</a>
76         {% endif %}
77     {% endfor %}
78     {% if posts.has_next %}
79         <a href="/inner_index?p={{ posts.next_page_number }}">下一页</a>
80     {% else %}
81         <a href="#">下一页</a>
82     {% endif %}
83     <span>{{ posts.number }}/{{ posts.paginator.num_pages }}</span>
84 </ul>
85 
86 </body>
87 </html>
View Code

  4.自定义分页组件

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

  1 ***************views.py*********************
  2 
  3 from app01.diy_per_page import *   #diy_per_page为我们自己单独写的分页组件文件
  4 uselist = []
  5 for i in range(1,923):
  6     use_data ={"username":"root"+str(i),"age":i}
  7     uselist.append(use_data)
  8 
  9 def diy_index(request):
 10     current_page = request.GET.get("p")
 11     obj = Diy_page(len(uselist),current_page,10,11)
 12     data = uselist[obj.start_page():obj.end_page()]
 13     return render(request,"diy_index.html",{"object_list":data,"obj":obj})
 14 
 15 *****************diy_per_page.py******************
 16 class Diy_page(object):
 17     def __init__(self,total_count,current_page,per_page_num,max_page_num):
 18         # 数据源总记录条数
 19         self.total_count = total_count
 20         # 接收用户发送的当前页码
 21         try:
 22             v = int(current_page)
 23             if v<=0:  #如果用户输入的页码小于1,直接让它等于1
 24                 v = 1
 25             self.current_page = v
 26         except Exception as  e:
 27             self.current_page = 1
 28         # 每一页显示的记录条数
 29         self.per_page_num = per_page_num
 30         # 每一页显示的最多页码数
 31         self.max_page_num = max_page_num
 32     @property
 33     def num_pages(self):
 34         '''
 35         总页码数
 36         :return:
 37         '''
 38         a,b = divmod(self.total_count,self.per_page_num)
 39         if b == 0:
 40             return a
 41         else:
 42             return a+1
 43     def page_range(self):
 44         '''
 45         每一页所能显示的最多页码数范围
 46         :return:
 47         '''
 48         if self.num_pages<self.max_page_num:  #如果总页码小于每一页显示的最多页码总数
 49             return range(1,self.num_pages+1)
 50         part = int(self.max_page_num/2)
 51         if self.current_page <= part:  #如果当前页小于等于每页显示最多页码数的一半
 52             return range(1,self.max_page_num+1)
 53         if self.current_page+part>self.num_pages:   #如果当前页加每页显示最多页码数的一半大于总页码数
 54             return range(self.num_pages-self.max_page_num,self.num_pages+1)
 55         return range(self.current_page-part,self.current_page+part+1)
 56     def start_page(self):
 57         '''
 58         切片的起始位置
 59         :return:
 60         '''
 61         return (self.current_page-1)*self.per_page_num
 62     def end_page(self):
 63         '''
 64         切片的结束位置
 65         :return:
 66         '''
 67         return self.current_page*self.per_page_num
 68     def a_num_pages(self):
 69         '''
 70         每一页底部页码链接标签
 71         :return:
 72         '''
 73         li = []
 74         head_page = "<li><a href='/diy_index?p=1'>首页</a></li>"
 75         li.append(head_page)
 76         if self.current_page ==1:
 77             prev_page = "<li><a href='#'>上一页</a></li>"   #生成上一页标签
 78         else:
 79             prev_page = "<li><a href='/diy_index?p=%s'>上一页</a></li>"%(self.current_page-1)
 80         li.append(prev_page)
 81         for row in self.page_range():
 82             if row == self.current_page:
 83                 data = "<li class='active'><a href='/diy_index?p=%s'>%s</a></li>"%(row,row)
 84             else:
 85                 data = "<li><a href='/diy_index?p=%s'>%s</a></li>" % (row, row)
 86             li.append(data)
 87         if self.current_page ==self.num_pages:
 88             next_page = "<li><a href='#'>下一页</a></li>"  #生成下一页标签
 89         else:
 90             next_page = "<li><a href='/diy_index?p=%s'>下一页</a><li>"%(self.current_page+1)
 91         li.append(next_page)
 92         last_page = "<li><a href='/diy_index?p=%s'>尾页</a><li>"%self.num_pages
 93         li.append(last_page)
 94         return "".join(li)  #返回前端是拼接的字符串
 95 
 96 
 97 ***************html**************************
 98 <!DOCTYPE html>
 99 <html lang="en">
100 <head>
101     <meta charset="UTF-8">
102     <title>Title</title>
103     <link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css">  #为了格式好看,我们引入bootstrap文件,调用其分页插件,相应标签属性对应修改,在此不加详细阐述
104 </head>
105 <body>
106 
107 <ul>
108     {% for i in object_list%}
109         <li>{{ i.username }}-{{ i.age }}</li>
110     {% endfor %}
111 
112     <ul class="pagination">
113         {{ obj.a_num_pages|safe }}
114      </ul>
115 </ul>
116 
117 </body>
118 </html>
View Code

Form组件

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

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

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

   (2)生成html代码

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

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

创建基本form

 1 ********************form类****************
 2 
 3 from django.shortcuts import render,HttpResponse,redirect
 4 from django import forms
 5 from django.forms import fields
 6 from app02 import models
 7 # Create your views here.
 8 
 9 
10 class Myform(forms.Form):  #定义form类
11     username = fields.CharField(
12         required=True,
13         max_length=32,
14         min_length=6,
15         label="用户名",
16         initial="请输入用户名",
17         error_messages={
18             "required":"不能为空",   #用定义的属性字段来定义错误信息
19             "invalid":"格式错误"    #格式错误都用invalid定义
20         }
21 
22     )
23     passwd = fields.IntegerField(
24         required=True,
25         label="密码"
26     )
27     e_mail = fields.EmailField(
28         required=True,
29         label="邮箱"
30 
31 
32     )
33 
34 ****************views处理函数*************
35 def myform1(request):
36     user_list = models.User_info.objects.all()
37     return render(request,"my_form1.html",{"user_list":user_list})
38 
39 def add_user(request):
40     if request.method == "GET":
41         obj = Myform()    #此处实例化form对象,对象里的字段可以通过静态方法调用,且在前端调用时会执行内部的__str__()方法,生成对应的标签
42         return render(request,"add_user.html",{"obj":obj})
43     else:
44         obj = Myform(request.POST)  #此处会将前端接收的数据传递给form类,此时并不会进行验证
45         if obj.is_valid():  #此处进行验证
46             models.User_info.objects.create(**obj.cleaned_data) #cleaned_data为验证过的用户数据,为字典格式
47             return redirect("/my_form1")
48         else:
49             return render(request,"add_user.html",{"obj":obj})
50 
51 def edit_user(request):
52     nid = request.GET.get("p")
53     if request.method == "GET":
54         obj = models.User_info.objects.filter(id = nid).first()
55         data = Myform({"username":obj.username,"passwd":obj.passwd,"e_mail":obj.e_mail})    #实例化时传入自定义字典可以在前端设置默认值,字段要跟form对应上
56         return render(request,"edit_user.html",{"data":data,"nid":nid})
57 
58     else:
59         data = Myform(request.POST)
60         if data.is_valid():
61             models.User_info.objects.filter(id = nid).update(**data.cleaned_data)
62             return redirect("/my_form1")
63         else:
64             return render(request,"edit_user.html",{"data":data,"nid":nid})
65 
66 
67 
68 ********************html*************************
View 
 1 **************my_form1****************
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body>
 8 <a href="/add_user">添加</a>
 9 <ul>
10     {% for row in user_list %}
11     <li>{{ row.username }}-{{ row.passwd }}-{{ row.e_mail }} <a href="/edit_user?p={{ row.id }}">修改</a></li>
12 {% endfor %}
13 </ul>
14 
15 </body>
16 </html>
17 
18 
19 ****************edit_user*******************
20 <!DOCTYPE html>
21 <html lang="en">
22 <head>
23     <meta charset="UTF-8">
24     <title>Title</title>
25 </head>
26 <body>
27 
28 <form action="/edit_user?p={{ nid }}" method="post" novalidate>
29     {% csrf_token %}
30     <p>{{ data.username.label }}{{ data.username }}{{ data.username.errors.0 }}</p>
31     <p>{{ data.passwd.label }}{{ data.passwd }}{{ data.passwd.errors.0 }}</p>
32     <p>{{ data.e_mail.label }}{{ data.e_mail }}{{ data.e_mail.errors.0 }}</p>
33     <p><input type="submit"></p>
34 </form>
35 
36 </body>
37 </html>
38 
39 ***************add_user*********************
40 <!DOCTYPE html>
41 <html lang="en">
42 <head>
43     <meta charset="UTF-8">
44     <title>Title</title>
45 </head>
46 <body>
47 <form action="/add_user" method="post" novalidate>
48     {% csrf_token %}
49     <p>{{ obj.username.label }}{{ obj.username }}{{ obj.username.errors.0 }}</p>
50     <p>{{ obj.passwd.label }}{{ obj.passwd }}{{ obj.passwd.errors.0 }}</p>
51     <p>{{ obj.e_mail.label }}{{ obj.e_mail }}{{ obj.e_mail.errors.0 }}</p>
52     <p><input type="submit"></p>
53 </form>
54 
55 </body>
56 </html>
57 
58 html
HTML

  FORM类

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

1, django内置字段:

  1 Field
  2     required=True,               是否允许为空
  3     widget=None,                 HTML插件
  4     label=None,                  用于生成Label标签或显示内容
  5     initial=None,                初始值
  6     help_text='',                帮助信息(在标签旁边显示)
  7     error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
  8     show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
  9     validators=[],               自定义验证规则
 10     localize=False,              是否支持本地化
 11     disabled=False,              是否可以编辑
 12     label_suffix=None            Label内容后缀
 13   
 14   
 15 CharField(Field)
 16     max_length=None,             最大长度
 17     min_length=None,             最小长度
 18     strip=True                   是否移除用户输入空白
 19   
 20 IntegerField(Field)
 21     max_value=None,              最大值
 22     min_value=None,              最小值
 23   
 24 FloatField(IntegerField)
 25     ...
 26   
 27 DecimalField(IntegerField)
 28     max_value=None,              最大值
 29     min_value=None,              最小值
 30     max_digits=None,             总长度
 31     decimal_places=None,         小数位长度
 32   
 33 BaseTemporalField(Field)
 34     input_formats=None          时间格式化  
 35   
 36 DateField(BaseTemporalField)    格式:2015-09-01
 37 TimeField(BaseTemporalField)    格式:11:12
 38 DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 39   
 40 DurationField(Field)            时间间隔:%d %H:%M:%S.%f
 41     ...
 42   
 43 RegexField(CharField)
 44     regex,                      自定制正则表达式
 45     max_length=None,            最大长度
 46     min_length=None,            最小长度
 47     error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 48   
 49 EmailField(CharField)     
 50     ...
 51   
 52 FileField(Field)
 53     allow_empty_file=False     是否允许空文件
 54   
 55 ImageField(FileField)     
 56     ...
 57     注:需要PIL模块,pip3 install Pillow
 58     以上两个字典使用时,需要注意两点:
 59         - form表单中 enctype="multipart/form-data"
 60         - view函数中 obj = MyForm(request.POST, request.FILES)
 61   
 62 URLField(Field)
 63     ...
 64   
 65   
 66 BooleanField(Field) 
 67     ...
 68   
 69 NullBooleanField(BooleanField)
 70     ...
 71   
 72 ChoiceField(Field)
 73     ...
 74     choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
 75     required=True,             是否必填
 76     widget=None,               插件,默认select插件
 77     label=None,                Label内容
 78     initial=None,              初始值
 79     help_text='',              帮助提示
 80   
 81   
 82 ModelChoiceField(ChoiceField)
 83     ...                        django.forms.models.ModelChoiceField
 84     queryset,                  # 查询数据库中的数据
 85     empty_label="---------",   # 默认空显示内容
 86     to_field_name=None,        # HTML中value的值对应的字段
 87     limit_choices_to=None      # ModelForm中对queryset二次筛选
 88       
 89 ModelMultipleChoiceField(ModelChoiceField)
 90     ...                        django.forms.models.ModelMultipleChoiceField
 91   
 92   
 93       
 94 TypedChoiceField(ChoiceField)
 95     coerce = lambda val: val   对选中的值进行一次转换
 96     empty_value= ''            空值的默认值
 97   
 98 MultipleChoiceField(ChoiceField)
 99     ...
100   
101 TypedMultipleChoiceField(MultipleChoiceField)
102     coerce = lambda val: val   对选中的每一个值进行一次转换
103     empty_value= ''            空值的默认值
104   
105 ComboField(Field)
106     fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
107                                fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
108   
109 MultiValueField(Field)
110     PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
111   
112 SplitDateTimeField(MultiValueField)
113     input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
114     input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
115   
116 FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
117     path,                      文件夹路径
118     match=None,                正则匹配
119     recursive=False,           递归下面的文件夹
120     allow_files=True,          允许文件
121     allow_folders=False,       允许文件夹
122     required=True,
123     widget=None,
124     label=None,
125     initial=None,
126     help_text=''
127   
128 GenericIPAddressField
129     protocol='both',           both,ipv4,ipv6支持的IP格式
130     unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
131   
132 SlugField(CharField)           数字,字母,下划线,减号(连字符)
133     ...
134   
135 UUIDField(CharField)           uuid类型
136     ...
View Code

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

2, django内置插件

 1 TextInput(Input)
 2 NumberInput(TextInput)
 3 EmailInput(TextInput)
 4 URLInput(TextInput)
 5 PasswordInput(TextInput)
 6 HiddenInput(TextInput)
 7 Textarea(Widget)
 8 DateInput(DateTimeBaseInput)
 9 DateTimeInput(DateTimeBaseInput)
10 TimeInput(DateTimeBaseInput)
11 CheckboxInput
12 Select
13 NullBooleanSelect
14 SelectMultiple
15 RadioSelect
16 CheckboxSelectMultiple
17 FileInput
18 ClearableFileInput
19 MultipleHiddenInput
20 SplitDateTimeWidget
21 SplitHiddenDateTimeWidget
22 SelectDateWidget  

  常用选择插件

 1 # 单radio,值为字符串
 2 # user = fields.CharField(
 3 #     initial=2,
 4 #     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
 5 # )
 6   
 7 # 单radio,值为字符串
 8 # user = fields.ChoiceField(
 9 #     choices=((1, '上海'), (2, '北京'),),
10 #     initial=2,
11 #     widget=widgets.RadioSelect
12 # )
13   
14 # 单select,值为字符串
15 # user = fields.CharField(
16 #     initial=2,
17 #     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
18 # )
19   
20 # 单select,值为字符串
21 # user = fields.ChoiceField(
22 #     choices=((1, '上海'), (2, '北京'),),
23 #     initial=2,
24 #     widget=widgets.Select
25 # )
26   
27 # 多选select,值为列表
28 # user = fields.MultipleChoiceField(
29 #     choices=((1,'上海'),(2,'北京'),),
30 #     initial=[1,],
31 #     widget=widgets.SelectMultiple
32 # )
33   
34   
35 # 单checkbox
36 # user = fields.CharField(
37 #     widget=widgets.CheckboxInput()
38 # )
39   
40   
41 # 多选checkbox,值为列表
42 # user = fields.MultipleChoiceField(
43 #     initial=[2, ],
44 #     choices=((1, '上海'), (2, '北京'),),
45 #     widget=widgets.CheckboxSelectMultiple
46 # )

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

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

方式二:

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

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

  自定义验证规则

方法一:

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

方法二:

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

方法三:自定义方法

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

方法四:自定义整体验证

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

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

 1 from django.forms import Form
 2 from django.forms import widgets
 3 from django.forms import fields
 4   
 5 from django.core.validators import RegexValidator
 6   
 7   
 8 ############## 自定义字段 ##############
 9 class PhoneField(fields.MultiValueField):
10     def __init__(self, *args, **kwargs):
11         # Define one message for all fields.
12         error_messages = {
13             'incomplete': 'Enter a country calling code and a phone number.',
14         }
15         # Or define a different message for each field.
16         f = (
17             fields.CharField(
18                 error_messages={'incomplete': 'Enter a country calling code.'},
19                 validators=[
20                     RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'),
21                 ],
22             ),
23             fields.CharField(
24                 error_messages={'incomplete': 'Enter a phone number.'},
25                 validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')],
26             ),
27             fields.CharField(
28                 validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')],
29                 required=False,
30             ),
31         )
32         super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args,
33                                          **kwargs)
34   
35     def compress(self, data_list):
36         """
37         当用户验证都通过后,该值返回给用户
38         :param data_list:
39         :return:
40         """
41         return data_list
42   
43 ############## 自定义插件 ##############
44 class SplitPhoneWidget(widgets.MultiWidget):
45     def __init__(self):
46         ws = (
47             widgets.TextInput(),
48             widgets.TextInput(),
49             widgets.TextInput(),
50         )
51         super(SplitPhoneWidget, self).__init__(ws)
52   
53     def decompress(self, value):
54         """
55         处理初始值,当初始值initial不是列表时,调用该方法
56         :param value:
57         :return:
58         """
59         if value:
60             return value.split(',')
61         return [None, None, None]

序列化

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

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

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

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

 

posted @ 2018-11-11 19:22  微凉fjc  阅读(140)  评论(0编辑  收藏  举报