,多对多:
创建多对多:
方式一:自定义关系表
class Host(models.Model): nid=models.AutoField(primary_key=True) hostname=models.CharField(max_length=32,db_index=True) ip=models.GenericIPAddressField(db_index=True) port=models.IntegerField() b=models.ForeignKey('Business',to_field='id') #自定义关联 class HostToApp(models.Model): hobj=models.ForeignKey(to='Host',to_fields='nid') aobj=models.ForeignKey(to='Application',to_fields='id')
对表的操作和foreignkey一样,并且可以直接对这个表进行操作
HostToApp.objects.create(hobj_id=1,aobj_id=2)
方式二:自动创建关系表
1 class Host(models.Model): 2 nid=models.AutoField(primary_key=True) 3 hostname=models.CharField(max_length=32,db_index=True) 4 ip=models.GenericIPAddressField(db_index=True) 5 port=models.IntegerField() 6 b=models.ForeignKey('Business',to_field='id') 7 8 class Application(models.Model): 9 name=models.CharField(max_length=64) 10 auto=models.ManyToManyField("Host")
缺点就是无法直接对第三张表进行操作,只能间接的方式来操作并且也无法在这个表增加多余字段
#现获取对象
obj = Application.objects.get(id=1)
# 然后间接操作第三张表中的内容
#增加
obj.r.add(1)#Application=1 host=1 obj中已经存在了app id 所以这里的add=1就是host表中的id
obj.r.add(2)#Application=1 host=2
obj.r.add(2,3,4)#Application=1 host=2/3/4
obj.r.add(*[1,2,3,4])
#移除
obj.r.remove(1)
obj.r.remove(2,4)
obj.r.remove(*[1,2,3])
#清空obj中的app id =1的所有内容
obj.r.clear()
#更新
obj.r.set([3,5,7])
# 所有相关的主机对象“列表” QuerySet
obj.r.all()
增删改查例子
def app(reques): obj=models.Application.objects.all() print(obj) for i in obj: #查询 print(i.name,i.auto.all()) #增加app项目并且返回这个obj对象 #obj=models.Application.objects.create(name='oracle') #增加1,3,4host到app #obj.auto.add(*[1,4,3]) #删除 # obj=models.Application.objects.get(id=1) # obj.auto.remove(1) # return render(reques,'app.html',{'obj':obj}) return HttpResponse("ok")
获取用户请求头
def test(request): #查看源码 #<class 'django.core.handlers.wsgi.WSGIRequest'> from django.core.handlers.wsgi import WSGIRequest print(type(request)) #查询 for x,v in request.environ.items(): print(x,v) return HttpResponse("Ok")
结果(用到最多的就是HTTP_USER_AGENT,判断客户端用的什么设备类型):


1 ('TMP', 'C:\\Users\\ADMIN~1.MEN\\AppData\\Local\\Temp') 2 ('PYTHONIOENCODING', 'UTF-8') 3 ('COMPUTERNAME', 'MENKEYI') 4 ('wsgi.multiprocess', False) 5 ('PROCESSOR_LEVEL', '6') 6 ('RUN_MAIN', 'true') 7 ('HTTP_COOKIE', 'csrftoken=mjMaE6agb2Si4NrdFacQpUIkviQIFv3C; _ga=GA1.1.627222119.1494076915; Hm_lvt_a033dafefef77193d4d80cc7103c9772=1493689044,1494076914,1494228704,1'494597132') 8 ('USERDOMAIN', xxxxxxxx') 9 ('GOROOT', 'C:\\Go\\app') 10 ('SERVER_PROTOCOL', 'HTTP/1.1') 11 ('SERVER_SOFTWARE', 'WSGIServer/0.1 Python/2.7.9') 12 ('PSMODULEPATH', 'C:\\Windows\\system32\\WindowsPowerShell\\v1.0\\Modules\\') 13 ('SCRIPT_NAME', u'') 14 ('COMMONPROGRAMFILES', 'C:\\Program Files\\Common Files') 15 ('PROCESSOR_IDENTIFIER', 'Intel64 Family 6 Model 70 Stepping 1, GenuineIntel') 16 ('REQUEST_METHOD', 'GET') 17 ('wsgi.version', (1, 0)) 18 ('PROGRAMFILES', 'C:\\Program Files') 19 ('PROCESSOR_REVISION', '4601') 20 ('SYSTEMROOT', 'C:\\Windows') 21 ('QUERY_STRING', '') 22 ('PATH', 'C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Program Files (x86)\\Common Files\\NetSarang;C:\\Program Files (x86)\\Intel\\iCLS Client\\;C:\\Program Files\\Intel\\iCLS Client\\;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\DAL;C:\\Program Files (x86)\\Intel\\Intel(R) Management Engine Components\\IPT;C:\\Program Files\\Intel\\WiFi\\bin\\;C:\\Program Files\\Common Files\\Intel\\WirelessCommon\\;C:\\Program Files\\Hewlett-Packard\\SimplePass\\;C:\\Program Files\\Microsoft SQL Server\\110\\Tools\\Binn\\;C:\\Python27;C:\\Python27\\Scripts;C:\\Program Files (x86)\\MySQL\\MySQL Server 5.5\\bin;C:\\Python27\\Lib\\site-packages\\Django-1.7.7-py2.7.egg\\django\\bin;C:\\Program Files (x86)\\IDM Computer Solutions\\UltraEdit;C:\\Program Files\\Windows Imaging\\;C:\\Program Files (x86)\\OpenVPN\\bin;C:\\Program Files\\Git\\cmd;C:\\Go\\bin;d:\\Program Files (x86)\\erl5.10.4\\bin;D:\\Program Files (x86)\\RabbitMQ Server\\rabbitmq_server-3.2.4\\sbin') 23 ('PYTHONUNBUFFERED', '1') 24 ('PROGRAMFILES(X86)', 'C:\\Program Files (x86)') 25 ('#ENVKKPRBC_CMDILNE', '') 26 ('CONTENT_LENGTH', '') 27 ('HTTP_UPGRADE_INSECURE_REQUESTS', '1') 28 ('HTTP_CACHE_CONTROL', 'max-age=0') 29 ('HTTP_CONNECTION', 'keep-alive') 30 ('REMOTE_HOST', '') 31 ('TEMP', 'C:\\Users\\ADMIN~1.MEN\\AppData\\Local\\Temp') 32 ('REMOTE_ADDR', '127.0.0.1') 33 ('COMMONPROGRAMFILES(X86)', 'C:\\Program Files (x86)\\Common Files') 34 ('#ENVTSLOGRBCSHELLEXT3340', '64101024') 35 ('PROCESSOR_ARCHITECTURE', 'AMD64') 36 ('wsgi.url_scheme', 'http') 37 ('ALLUSERSPROFILE', 'C:\\ProgramData') 38 ('SHELLLAUNCH{A81BA54B-CCFE-4204-8E79-A68C0FDFA5CF}', 'ShellExt') 39 ('LOCALAPPDATA', 'C:\\Users\\admin.menkeyi\\AppData\\Local') 40 ('HOMEPATH', '\\Users\\admin.menkeyi') 41 ('USERDOMAIN_ROAMINGPROFILE', 'xxxxxx') 42 ('VS120COMNTOOLS', 'D:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\Common7\\Tools\\') 43 ('ERLANG_HOME', 'd:\\Program Files (x86)\\erl5.10.4') 44 ('USERNAME', 'admin') 45 ('HTTP_ACCEPT', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8') 46 ('#ENVTSLOGSSS3340', '64102944') 47 ('LOGONSERVER', '\\\\MENKEYI') 48 ('COMSPEC', 'C:\\Windows\\system32\\cmd.exe') 49 ('PROGRAMDATA', 'C:\\ProgramData') 50 ('wsgi.file_wrapper', <class wsgiref.util.FileWrapper at 0x0000000003A08E88>) 51 ('PYTHONPATH', 'E:\\mydjango') 52 ('RABBITMQ_SERVER', 'D:\\Program Files (x86)\\RabbitMQ Server') 53 ('wsgi.multithread', True) 54 ('wsgi.input', <socket._fileobject object at 0x0000000003C0AA20>) 55 ('HTTP_USER_AGENT', 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36') 56 ('HTTP_HOST', '127.0.0.1:8000') 57 ('SESSIONNAME', 'Console') 58 ('PATHEXT', '.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC') 59 ('PATH_INFO', u'/app01/test/') 60 ('FP_NO_HOST_CHECK', 'NO') 61 ('WINDIR', 'C:\\Windows') 62 ('#ENVTSLOGTSLOG3340', '64101664') 63 ('HTTP_ACCEPT_ENCODING', 'gzip, deflate, sdch, br') 64 ('SERVER_PORT', '8000') 65 ('MOZ_PLUGIN_PATH', 'C:\\Program Files (x86)\\Foxit Software\\Foxit Reader\\plugins\\') 66 ('HOMEDRIVE', 'C:') 67 ('SERVER_NAME', 'axxxxxi') 68 ('wsgi.run_once', False) 69 ('#ENVTSLOGXMEDIALIBRARY3340', '280591264') 70 ('SYSTEMDRIVE', 'C:') 71 ('GATEWAY_INTERFACE', 'CGI/1.1') 72 ('HTTP_ACCEPT_LANGUAGE', 'zh-CN,zh;q=0.8') 73 ('PYCHARM_HOSTED', '1') 74 ('NUMBER_OF_PROCESSORS', '8') 75 ('APPDATA', 'C:\\Users\\admin.menkeyi\\AppData\\Roaming') 76 ('DJANGO_SETTINGS_MODULE', 'mydjango.settings') 77 ('PROGRAMW6432', 'C:\\Program Files') 78 ('CONTENT_TYPE', 'text/plain') 79 ('wsgi.errors', <open file '<stderr>', mode 'w' at 0x00000000020FB150>) 80 ('COMMONPROGRAMW6432', 'C:\\Program Files\\Common Files') 81 ('OS', 'Windows_NT') 82 ('PUBLIC', 'C:\\Users\\Public') 83 ('USERPROFILE', 'C:\\Users\\admin')
分页(手动写)
view


1 def user_list(request): 2 #模拟数据库数据 3 users=range(200) 4 5 #从前端接受过来的都是字符串,request.GET.get('p',1)如果p为空那么值为1 6 #获取当前页 7 curr_pager_num=int(request.GET.get('p',1)) 8 9 #每个页面显示几条数据 10 page_data_num=10 11 12 13 14 #开始索引 15 start=(curr_pager_num-1)*page_data_num 16 #结束索引 17 end=curr_pager_num*page_data_num 18 #根据star和end做页面数据切分 19 data=users[start:end] 20 21 #获取数据总页数 22 all_count=len(users) 23 24 #计算分页数量 25 total_count,y=divmod(all_count,page_data_num) 26 #y>1分页就总数就加1 27 if y: 28 total_count += 1 29 30 #存储分页内容列表 31 page_str_list=[] 32 33 #页面页码尽量是基数不能小于5 34 pager_num=11 35 36 #总页数小于11 37 if total_count < pager_num : 38 start_index=1 39 end_index=total_count+1 40 41 else: 42 #当前页小于6 43 if curr_pager_num <6: 44 start_index=1 45 #end_index=11 + 1 46 end_index=pager_num + 1 47 else: 48 #总页数大于11:start_index= curr_pager_num -5 49 start_index= curr_pager_num - (pager_num - 1)/2 50 #end_index=curr_pager_num + 6 51 end_index=curr_pager_num + (pager_num + 1)/2 52 #curr_pager_num+ 5) 53 if (curr_pager_num+ (pager_num - 1)/2) > total_count: 54 #start_index=total_count - 11 + 1 55 start_index=total_count - pager_num + 1 56 end_index=total_count+1 57 58 59 if curr_pager_num ==1: 60 prev='<a class="page" href="javascript:void(0)">上一页</a>' 61 else: 62 #上一页 63 prev='<a class="page" href="/user_list/?p=%s">上一页</a>'%(curr_pager_num - 1) 64 page_str_list.append(prev) 65 #循环增加 66 for i in range(int(start_index),int(end_index)): 67 68 #当前页面class中增加active属性 69 if i == curr_pager_num: 70 temp='<a class="page active" href="/user_list/?p=%s">%s</a>'%(i,i) 71 else: 72 temp='<a class="page" href="/user_list/?p=%s">%s</a>'%(i,i) 73 74 #追加到列表 75 page_str_list.append(temp) 76 77 78 #当前页等于最大页数就不在加1 79 if curr_pager_num == total_count: 80 next_page='<a class="page" href="javascript:void(0)">下一页</a>' 81 else: 82 next_page='<a class="page" href="/user_list/?p=%s">下一页</a>'%(curr_pager_num + 1) 83 page_str_list.append(next_page) 84 85 86 #跳转页 87 jump=""" 88 <input type='text'/><a onclick='jumpTo(this,"/user_list/?p=")' id='jump_id'>GO</a> 89 <script> 90 function jumpTo(ths,base){ 91 var val = ths.previousSibling.value; 92 location.href = base + val; 93 } 94 </script> 95 """ 96 page_str_list.append(jump) 97 98 #将列表进行字符串拼接返回前端 99 page_str="".join(page_str_list) 100 101 102 103 #page_str=mark_safe("",join(page_list))前端将信任这代码 104 105 return render(request,'user_list.html',{'li':data,'page_str':page_str})
url
url(r'^user_list/$', views.user_list),
html,中的incloud 的html和上一个例子中的一样


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <style> 7 .pagediv .page{ 8 display: inline-block; 9 padding: 5px; 10 background-color: aqua; 11 margin: 5px; 12 } 13 .pagediv .page.active{ 14 background-color: darkgreen; } 15 </style> 16 </head> 17 <body> 18 19 <ul > 20 {% include 'li.html' %} 21 </ul> 22 <div class="pagediv"> 23 {{ page_str|safe }} 24 </div> 25 </body> 26 </html>


1 {% for user in li %} 2 <li> {{ user }}</li> 3 {% endfor %}
将上面的代码改造成类方式实现
view


1 from utlis import pagination 2 3 def user_list(request): 4 #模拟数据库数据 5 users=range(200) 6 7 #获取当前页 8 current_page = request.GET.get('p', 1) 9 #转成int 10 current_page = int(current_page) 11 #获取cookie展示的当前页,默认为10条数据 12 val = request.COOKIES.get('per_page_count',10) 13 val1 = request.COOKIES 14 print(val) 15 print(val1) 16 val = int(val) 17 18 19 page_obj = pagination.Page(current_page,len(users),val) 20 21 data = users[page_obj.start:page_obj.end] 22 23 page_str = page_obj.page_str("/user_list/") 24 25 result=render(request,'user_list.html',{'li':data,'page_str':page_str}) 26 result.set_cookie('per_page_count',val,path='/user_list/') 27 return result
封装类方法


1 #-*- coding:utf-8 -*- 2 from django.utils.safestring import mark_safe 3 4 5 class Page: 6 def __init__(self, current_page, data_count, per_page_count=10, pager_num=7): 7 #当前页 8 self.current_page = current_page 9 #数据总数 10 self.data_count = data_count 11 #每个页面显示几条数据,不是页码 12 self.per_page_count = per_page_count 13 #页码 14 self.pager_num = pager_num 15 16 @property 17 def start(self): 18 return (self.current_page - 1) * self.per_page_count 19 20 @property 21 def end(self): 22 return self.current_page * self.per_page_count 23 24 @property 25 def total_count(self): 26 v, y = divmod(self.data_count, self.per_page_count) 27 if y: 28 v += 1 29 return v 30 31 def page_str(self, base_url): 32 page_list = [] 33 34 if self.total_count < self.pager_num: 35 start_index = 1 36 end_index = self.total_count + 1 37 else: 38 if self.current_page <= (self.pager_num + 1) / 2: 39 start_index = 1 40 end_index = self.pager_num + 1 41 else: 42 start_index = self.current_page - (self.pager_num - 1) / 2 43 end_index = self.current_page + (self.pager_num + 1) / 2 44 if (self.current_page + (self.pager_num - 1) / 2) > self.total_count: 45 end_index = self.total_count + 1 46 start_index = self.total_count - self.pager_num + 1 47 48 if self.current_page == 1: 49 prev = '<a class="page" href="javascript:void(0);">上一页</a>' 50 else: 51 prev = '<a class="page" href="%s?p=%s">上一页</a>' % (base_url, self.current_page - 1,) 52 page_list.append(prev) 53 54 for i in range(int(start_index), int(end_index)): 55 if i == self.current_page: 56 temp = '<a class="page active" href="%s?p=%s">%s</a>' % (base_url, i, i) 57 else: 58 temp = '<a class="page" href="%s?p=%s">%s</a>' % (base_url, i, i) 59 page_list.append(temp) 60 61 if self.current_page == self.total_count: 62 nex = '<a class="page" href="javascript:void(0);">下一页</a>' 63 else: 64 nex = '<a class="page" href="%s?p=%s">下一页</a>' % (base_url, self.current_page + 1,) 65 page_list.append(nex) 66 67 jump = """ 68 <input type='text' /><a onclick='jumpTo(this, "%s?p=");'>GO</a> 69 <script> 70 function jumpTo(ths,base){ 71 var val = ths.previousSibling.value; 72 location.href = base + val; 73 } 74 </script> 75 """ % (base_url,) 76 77 page_list.append(jump) 78 79 page_str = mark_safe("".join(page_list)) 80 81 return page_str
html,incloud hst


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <style> 7 .pagediv .page{ 8 display: inline-block; 9 padding: 5px; 10 background-color: aqua; 11 margin: 5px; 12 } 13 .pagediv .page.active{ 14 background-color: darkgreen; } 15 </style> 16 </head> 17 <body> 18 19 <ul > 20 {% include 'li.html' %} 21 </ul> 22 <div> 23 <select id="ps" onchange="changePageSize(this)"> 24 <option value="10">10</option> 25 <option value="30">30</option> 26 <option value="50">50</option> 27 <option value="100">100</option> 28 </select> 29 </div> 30 <div class="pagediv"> 31 {{ page_str|safe }} 32 </div> 33 <script src="/static/jquery-1.12.4.js"></script> 34 <script src="/static/jquery.cookie.js"></script> 35 <script> 36 37 $(function(){ 38 var v= $.cookie('per_page_count'); 39 40 $('#ps').val(v); 41 }); 42 43 function changePageSize(ths){ 44 var v = $(ths).val(); 45 console.log(v); 46 $.cookie('per_page_count',v, {'path': "/user_list/"}); 47 location.reload(); 48 } 49 </script> 50 </body> 51 </html>
cookie操作
实现简单的cookie操作
url(r'^index/', views.index), url(r'^login/$', views.login),
def index(request): v=request.COOKIES.get('cookie_username') if v: return render(request,'index.html',{'username':v}) else: return redirect('/app01/login/') def login(request): user_data={ 'test1':'123456', 'test2':'123123', } if request.method == 'GET': return render(request,'login.html') if request.method == 'POST': username=request.POST.get('username') #py2中取回参数默认是u编码,转成utf-8 password=request.POST.get('password').encode(encoding='utf-8') if user_data.get(username): if password == user_data[username]: #获取对象,用render的方法也是这样 res=redirect('/app01/index/') #设置对象cookie的K,V信息 res.set_cookie('cookie_username',username) #返回对象 return res return render(request,'login.html'
页面结果:
cookie参数设置
1 rep = HttpResponse(...) 或 rep = render(request, ...) 2 3 rep.set_cookie(key,value,...) 4 rep.set_signed_cookie(key,value,salt='加密盐',...) 5 参数: 6 key, 键 7 value='', 值 8 max_age=None, 超时时间 9 expires=None, 超时时间(IE requires expires, so set it if hasn't been already.) 10 path='/', Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问 11 domain=None, Cookie生效的域名 12 secure=False, https传输是为True 13 httponly=False 只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖)
由于cookie保存在客户端的电脑上,所以,JavaScript和jquery也可以操作cookie
<script src='/static/js/jquery.cookie.js'></script> $.cookie("list_pager_num", 30,{ path: '/' });
加密cookie(带签名)


1 加密 2 request.COOKIES['key'] 3 request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None) 4 参数: 5 default: 默认值 6 salt: 加密盐 7 max_age: 后台控制过期时间 8 9 解密 10 request.get_signed_cookie('key',salt='这里需要和加密的值一样')
利用cookie做登录装饰器


1 FBV: 2 #装饰器函数 3 def auth(func): 4 def inner(reqeust,*args,**kwargs): 5 v = reqeust.COOKIES.get('username') 6 if not v: 7 return redirect('/login/') 8 return func(reqeust, *args,**kwargs) 9 return inner 10 11 12 @auth 13 def xxx(request,*args,**kwargs): 14 15 @auth 16 def xxx1(request,*args,**kwargs) 17 18 19 20 21 22 CBV: 23 from django import views 24 from django.utils.decorators import method_decorator 25 26 #这个方法主要是不用写下面的dispatch方法。调用auth方法执行在Order类下的dispatch方法。这样在这个类下的所有方法对会登录验证 27 @method_decorator(auth,name='dispatch') 28 class Order(views.View): 29 #自己写dispatch方法,在方法前面增加@method_decorator(auth)装饰器 30 # @method_decorator(auth) 31 # def dispatch(self, request, *args, **kwargs): 32 # return super(Order,self).dispatch(request, *args, **kwargs) 33 34 # @method_decorator(auth) 35 def get(self,reqeust): 36 v = reqeust.COOKIES.get('username111') 37 return render(reqeust,'index.html',{'current_user': v}) 38 39 def post(self,reqeust): 40 v = reqeust.COOKIES.get('username111') 41 return render(reqeust,'index.html',{'current_user': v})
PS:不设置超时默认 关闭浏览器就没有效果了
Session
cookie是保存在客户浏览器中的键值对
session是保存在服务器端的键值对
利用session做一个登陆
url


1 from django.conf.urls import include, url 2 from django.contrib import admin 3 from app03 import views 4 urlpatterns = [ 5 url(r'^admin/', include(admin.site.urls)), 6 url(r'^login',views.login), 7 url(r'^index',views.index), 8 ]
view


1 #-*- coding=utf-8 -*- 2 from django.shortcuts import render,HttpResponse,redirect 3 4 # Create your views here. 5 6 7 def index(request): 8 #获取当前用户随机字符串 9 #根据随机字符串查询对应信息 10 if request.session['is_login']: 11 return HttpResponse("ok") 12 else: 13 return redirect('/login/') 14 15 16 def login(request): 17 if request.method == 'GET': 18 return render(request,'login.html') 19 elif request.method == 'POST': 20 user = request.POST.get('user').encode(encoding='utf-8') 21 password =request.POST.get('pass').encode(encoding='utf-8') 22 print(user,password) 23 if user == 'root' and password == '123456': 24 #生成随机字符串 25 #写到客户端浏览器cookie 26 #内存中创建session 27 #在随机字符串对应的字典中生成信息 28 #创建session必须先初始化数据库,python manage.py migrate 29 request.session['username']=user 30 request.session['is_login']=True 31 return redirect('/index/') 32 else: 33 return render(request,'login.html')
template


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <form action="/login/" method="POST" > 9 <p> 10 <input type="text" name="user" placeholder="用户名" /> 11 </p> 12 <p> 13 <input type="password" name="pass" placeholder="密码" /> 14 </p> 15 16 <input type="submit" value="提交"/> 17 </form> 18 </body> 19 </html>
session缓存
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:
数据库(默认)
缓存
文件
缓存+数据库
加密cookie
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
1数据库


1 Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 2 3 a. 配置 settings.py 4 5 SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) 6 7 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) 8 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) 9 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) 10 SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) 11 SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) 12 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) 13 SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) 14 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,例如超时时间10秒,刷新一下网页那么这个超时时间就继续加10秒,这个功能默认关闭 15 16 17 18 b. 使用 19 20 def index(request): 21 # 获取 22 request.session['k1'] 23 #获取k1,如果值不存在值设置为None 24 request.session.get('k1',None) 25 #不论k1是否存在都设置k/v 26 request.session['k1'] = 123 27 #存在不设置 28 request.session.setdefault('k1',123) 29 #删除k 30 del request.session['k1'] 31 32 # 所有 键、值、键值对 33 request.session.keys() 34 request.session.values() 35 request.session.items() 36 request.session.iterkeys() 37 request.session.itervalues() 38 request.session.iteritems() 39 40 #删除所有 41 request.session.clear() 42 43 # 用户session的随机字符串,这个很少能用到 44 request.session.session_key 45 46 #如果session没有超过超时日期,但是客户端自己清除了cookie.这样在此登录数据库中就会存在一条新的session信息,产生了垃圾数据 47 #首先哪到所有session,然后失效的日期<当前日期我们就 把这个数据删除掉 48 request.session.clear_expired() 49 50 # 检查 用户session的随机字符串 在数据库中是否 51 request.session.exists("session_key") 52 53 # 删除当前用户的所有Session数据 54 request.session.delete("session_key") 55 56 #默认2周超时时间 57 request.session.set_expiry(value) 58 * 如果value是个整数,session会在些秒数后失效。 59 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 60 * 如果value是0,用户关闭浏览器session就会失效。 61 * 如果value是None,session会依赖全局session失效策略。
2缓存session(memcache/内存)


1 配置 settings.py 2 3 SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 4 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置default就是chaches下的default方法 5 6 7 # 此缓存使用python-memcached模块连接memcache 8 #ps可以在CACHES写多个不同的缓存方式, 9 10 CACHES = { 11 'default': { 12 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 13 'LOCATION': '127.0.0.1:11211', 14 } 15 } 16 17 CACHES = { 18 'default1': { 19 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 20 'LOCATION': 'unix:/tmp/memcached.sock', 21 } 22 } 23 24 CACHES = { 25 'default2': { 26 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 27 'LOCATION': [ 28 '172.19.26.240:11211', 29 '172.19.26.242:11211', 30 ] 31 } 32 }


1 # 此缓存将内容保存至内存的变量中 2 # 配置: 3 CACHES = { 4 'default': { 5 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 6 'LOCATION': 'unique-snowflake', 7 } 8 } 9 10 # 注:其他配置同开发调试版本


1 # 此缓存使用pylibmc模块连接memcache 2 3 CACHES = { 4 'default': { 5 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 6 'LOCATION': '127.0.0.1:11211', 7 } 8 } 9 10 CACHES = { 11 'default': { 12 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 13 'LOCATION': '/tmp/memcached.sock', 14 } 15 } 16 17 CACHES = { 18 'default': { 19 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 20 'LOCATION': [ 21 '172.19.26.240:11211', 22 '172.19.26.242:11211', 23 ] 24 } 25 }
3文件缓存


1 # 此缓存将内容保存至文件 2 # 配置: 3 4 CACHES = { 5 'default': { 6 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 7 'LOCATION': '/var/tmp/django_cache', 8 } 9 } 10 # 注:其他配置同开发调试版本
4缓存+数据库


1 数据库用于做持久化,缓存用于提高效率。先找cache在找数据库 2 3 a. 配置 settings.py 4 5 SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 6
5加密cookie


1 配置 settings.py,就将session加密后放置在cookie中 2 3 SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
简单例子
csrf
举例:
CSRF攻击的主要目的是让用户在不知情的情况下攻击自己已登录的一个系统,类似于钓鱼。如用户当前已经登录了邮箱,或bbs,同时用户又在使用另外一个,已经被你控制的站点,我们姑且叫它钓鱼网站。这个网站上面可能因为某个图片吸引你,你去点击一下,此时可能就会触发一个js的点击事件,构造一个bbs发帖的请求,去往你的bbs发帖,由于当前你的浏览器状态已经是登陆状态,所以session登陆cookie信息都会跟正常的请求一样,纯天然的利用当前的登陆状态,让用户在不知情的情况下,帮你发帖或干其他事情。
解决办法:
在post请求的时候加上django给的随机字符串
1、form提交


1 <form action="/login/" method="POST" > 2 {% csrf_token %} 3 <p> 4 <input type="text" name="user" placeholder="用户名" /> 5 </p> 6 <p> 7 <input type="password" name="pass" placeholder="密码" /> 8 </p> 9 <input type="checkbox" name='timeoutval' value="1" />10秒钟免登陆 10 <input type="submit" value="提交"/> 11 </form>
返回头中有csrftoken
form也有token,django自动设置了一个隐藏的input
2、ajax 提交


1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <form action="/login/" method="POST" > 9 <p> 10 <input type="text" id="username" placeholder="用户名" /> 11 </p> 12 <p> 13 <input type="password" id="password" placeholder="密码" /> 14 </p> 15 <input type="checkbox" name='timeoutval' value="1" />10秒钟免登陆 16 <input id="btn" type="button" value="提交"/> 17 </form> 18 <script src="/static/jquery-1.12.4.js"></script> 19 <script src="/static/jquery.cookie.js"></script> 20 <script> 21 $(function(){ 22 23 //验证ajax请求头 24 function csrfSafeMethod(method) { 25 // GET|HEAD|OPTIONS|TRACE 这4种请求头不需要加token 26 return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 27 } 28 29 30 //在执行任何ajax提交之前都会先执行这个方法增加token。这样相当于对所有ajax进行了一次装饰 31 $.ajaxSetup({ 32 beforeSend:function(xhr,settings){ 33 //判断请求方法是否需要增加token 34 if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 35 xhr.setRequestHeader('X-CSRFtoken', $.cookie('csrftoken')); 36 } 37 } 38 }); 39 40 41 $('#btn').click(function(){ 42 var token=$.cookie('csrftoken') 43 console.log(token) 44 45 var username = $('#username').val() 46 var password = $('#password').val() 47 $.ajax({ 48 url:'/login/', 49 type:"POST", 50 data:{"user":username,'pwd':password}, 51 //headers:{'X-CSRFtoken':$.cookie('csrftoken')},少量方法可以 52 success:function(arg){ 53 54 } 55 }) 56 }) 57 }) 58 </script> 59 </body> 60 </html>
中间件
拿一下中间件举例,中间件的结大致可以分成reqeust和response。request请求从下往上走,最后到view视图,然后在从view返回response从下往上
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)
每个中间件都是一个简单的python类,类中定义了很多如下的方法:
process_request(self,request)
在django决定调用哪个视图函数之前
需要返回一个值,可以是None或者HttpResponse对象。如果返回None,则继续执行。如果返回HttpResponse对象,则停止执行,返回HttpResponse对象。
process_view(self,request,view_func,view_args,view_kwargs)
view_func是django选择的视图处理函数(是一个函数对象,而不是一个简单的字符串),而最后两个则是非关键字参数和关键字参数。
它会在调用视图函数之前调用。也是返回None或者HttpResponse对象
process_template_response(self,request,response)
在render()调用之后,模板调用之前。
process_response(self,request,response)
始终会被调用,在响应时被第一个调用。
process_exception(self,request,exception)
在异常视图函数调用前调用
代码
代码结构
中间件


1 #-*- coding=utf-8 -*- 2 from django.shortcuts import HttpResponse 3 class midd_one(object): 4 def process_request(self, request): 5 print("process_request张三") 6 #在request时候return 那么将不会在继续执行中间件了 7 #return HttpResponse("再见") 8 def process_response(self, request, response): 9 print("process_response张三") 10 return response 11 12 class midd_two(object): 13 def process_request(self, request): 14 print("process_request李四") 15 16 def process_response(self, request, response): 17 print("process_response李四") 18 return response
view


1 def test(request): 2 print("view_func") 3 return HttpResponse("ok")
结果:这样顺序就很容看出来
process_request张三
process_request李四
view_func
process_response李四
process_response张三
这里在做一下更改
class midd_one(object): def process_request(self, request): print("process_request张三") #让张三直接返回结果 return HttpResponse("再见") def process_response(self, request, response): print("process_response张三") return response
结果:
process_request张三
process_response李四
process_response张三
张三的return 会直接传递给李四的response然后在往上传递这个值,django1.7 1.8 1.9 都是按照这个方法做的,django10 以上就不一样了 张三就直接返回response
再看下process_template_response方法(这个方法用的不多):
div1.py
class midd_two(object): def process_request(self, request): print("process_request李四") def process_view(self,request,view_func,view_args,view_kwargs): print("process_view 李四") def process_response(self, request, response): print("process_response李四") return response #当view函数用render返回的时候才会执行这个 def process_template_response(self,request,response): print("process_template_response李四") return response
view
#伪造一个类然后里面有render方法
class Foo(object): def render(self): return HttpResponse("OK") def test(request): print("view_func") return Foo()
结果:
process_request张三
process_request李四
process_view 张三
process_view 李四
view_func
process_template_response李四
process_response李四
process_response张三
在来看下process_exception不常用:
div1.py view中报错才会执行process_exception
class midd_two(object): def process_request(self, request): print("process_request李四") def process_view(self,request,view_func,view_args,view_kwargs): print("process_view 李四") def process_response(self, request, response): print("process_response李四") return response def process_template_response(self,request,response): print("process_template_response李四") return response def process_exception(self,request,exception): print("process_exception李四")
view
def test(request): int(a)#故意出错 print("view_func") return HttpResponse("ok")
结果:
process_request张三
process_request李四
process_view 张三
process_view 李四
process_exception李四
process_response李四
process_response张三
缓存
1开发测试版缓存
2内存
3文件
4数据库
1基础


1 # 此为开始调试用,实际内部不做任何操作 2 # 通用配置: 3 CACHES = { 4 'default': { 5 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎有很多,下面的参数都是通用的 6 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 7 'OPTIONS':{ 8 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 9 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) 10 }, 11 'KEY_PREFIX': '', # 缓存key的前缀(默认空) 12 'VERSION': 1, # 缓存key的版本(默认1) 13 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】) 14 } 15 } 16 17 18 # 底层实现获取和定义key方法,想更改key的格式就可以修改这里 19 def default_key_func(key, key_prefix, version): 20 """ 21 Default function to generate keys. 22 23 Constructs the key used by all other methods. By default it prepends 24 the `key_prefix'. KEY_FUNCTION can be used to specify an alternate 25 function with custom key making behavior. 26 """ 27 return '%s:%s:%s' % (key_prefix, version, key) 28 29 def get_key_func(key_func): 30 """ 31 Function to decide which key function to use. 32 33 Defaults to ``default_key_func``. 34 """ 35 if key_func is not None: 36 if callable(key_func): 37 return key_func 38 else: 39 return import_string(key_func) 40 return default_key_func
2内存


1 # 此缓存将内容保存至内存的变量中 2 # 配置: 3 CACHES = { 4 'default': { 5 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 6 'LOCATION': 'unique-snowflake', 7 } 8 } 9 10 11 'LOCATION': 'unique-snowflake', 定义的唯一key,底层就是放在了一个大的字典中
3文件


1 # 此缓存将内容保存至文件 2 # 配置: 3 CACHES = { 4 'default': { 5 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 6 'LOCATION': os.path.join(BASE_DIR,'cache'), 7 } 8 } 9 10 # 注:其他配置同开发调试版本
4数据库


1 # 此缓存将内容保存至数据库 2 3 # 配置: 4 CACHES = { 5 'default': { 6 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 7 'LOCATION': 'my_cache_table', # 数据库表 8 } 9 } 10 11 # 注:执行创建表命令 python manage.py createcachetable
5 memcache(python-memcached模块)


1 # 此缓存使用python-memcached模块连接memcache 2 3 CACHES = { 4 'default': { 5 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 6 'LOCATION': '127.0.0.1:11211', 7 } 8 } 9 10 CACHES = { 11 'default': { 12 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 13 'LOCATION': 'unix:/tmp/memcached.sock', 14 } 15 } 16 17 CACHES = { 18 'default': { 19 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 20 'LOCATION': [ 21 '172.19.26.240:11211', 22 '172.19.26.242:11211', 23 ] 24 } 25 #权重设置 26 CACHES = { 27 'default': { 28 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 29 'LOCATION': [ 30 ('172.19.26.240:11211',2), 31 ('172.19.26.242:11211',16), 32 ] 33 } 34 }
memcache(pylibmc模块)


1 # 此缓存使用pylibmc模块连接memcache 2 3 CACHES = { 4 'default': { 5 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 6 'LOCATION': '127.0.0.1:11211', 7 } 8 } 9 10 CACHES = { 11 'default': { 12 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 13 'LOCATION': '/tmp/memcached.sock', 14 } 15 } 16 17 CACHES = { 18 'default': { 19 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 20 'LOCATION': [ 21 '172.19.26.240:11211', 22 '172.19.26.242:11211', 23 ] 24 } 25 }
缓存应用
1独立视图缓存
新建cache目录
setting


1 CACHES = { 2 'default': { 3 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 4 'LOCATION': os.path.join(BASE_DIR,'cache'), 5 } 6 }
view


1 from django.views.decorators.cache import cache_page 2 #缓存10秒 3 @cache_page(10) 4 def cache(request): 5 import time 6 cachedata = time.time() 7 cachedata1 = time.time() 8 return render(request,'cache.html',{'cachedata':cachedata,'cachedata1':cachedata1})
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>{{ cachedata }}</h1> <h1>{{ cachedata1 }}</h1> </body> </html>
2局部页面缓存


1 {% load cache %} 2 <!DOCTYPE html> 3 <html lang="en"> 4 <head> 5 <meta charset="UTF-8"> 6 <title>Title</title> 7 </head> 8 <body> 9 //key_idv是key的名字,但是这个名字需要跟setting中设置的前缀组合起来 10 {% cache 5 key_idv %} 11 <h1>{{ cachedata }}缓存5秒</h1> 12 {% endcache %} 13 <h1>{{ cachedata1 }}</h1> 14 </body> 15 </html>
3全站缓存(适合基本不改变的如博客)


1 使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 2 3 MIDDLEWARE = [ 4 'django.middleware.cache.UpdateCacheMiddleware', 5 # 其他中间件... 6 'django.middleware.cache.FetchFromCacheMiddleware', 7 ] 8 9 CACHE_MIDDLEWARE_ALIAS = "" 10 CACHE_MIDDLEWARE_SECONDS = "" 11 CACHE_MIDDLEWARE_KEY_PREFIX = ""
信号
django内置信号


1 Model signals 2 pre_init # django的modal执行其构造方法前,自动触发 3 post_init # django的modal执行其构造方法后,自动触发 4 pre_save # django的modal对象保存前,自动触发 5 post_save # django的modal对象保存后,自动触发 6 pre_delete # django的modal对象删除前,自动触发 7 post_delete # django的modal对象删除后,自动触发 8 m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发 9 class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 10 Management signals 11 pre_migrate # 执行migrate命令前,自动触发 12 post_migrate # 执行migrate命令后,自动触发 13 Request/response signals 14 request_started # 请求到来前,自动触发 15 request_finished # 请求结束后,自动触发 16 got_request_exception # 请求异常后,自动触发 17 Test signals 18 setting_changed # 使用test测试修改配置文件时,自动触发 19 template_rendered # 使用test测试渲染模板时,自动触发 20 Database Wrappers 21 connection_created # 创建数据库连接时,自动触发
对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:
启动django的时候会加载app下的__init__.py,因为注册信号必须是自动加载,这样django才可用。不一定非的写在这里。


1 from django.core.signals import request_finished 2 from django.core.signals import request_started 3 from django.core.signals import got_request_exception 4 5 from django.db.models.signals import class_prepared 6 from django.db.models.signals import pre_init, post_init 7 from django.db.models.signals import pre_save, post_save 8 from django.db.models.signals import pre_delete, post_delete 9 from django.db.models.signals import m2m_changed 10 from django.db.models.signals import pre_migrate, post_migrate 11 12 from django.test.signals import setting_changed 13 from django.test.signals import template_rendered 14 15 from django.db.backends.signals import connection_created 16 17 18 #request请求完事的方法 19 def callback(sender, **kwargs): 20 print("xxoo_callback") 21 print(sender,kwargs) 22 23 #完成请求后执行callback方法 24 request_finished.connect(callback)
结果:
test_sg
xxoo_callback
(<class 'django.core.handlers.wsgi.WSGIHandler'>, {'signal': <django.dispatch.dispatcher.Signal object at 0x0000000002AC5B70>})
自定义信号
目录结构


1 import django.dispatch 2 pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"]) 3 4 5 6 def callback(sender, **kwargs): 7 print("diy_callback") 8 print(sender,kwargs) 9 10 pizza_done.connect(callback)
view.py


1 #导入自定义信号 2 from sg.test_sg import pizza_done 3 def test_sg(request): 4 #sender ,toppings, size随便写 5 pizza_done.send(sender='seven',toppings=123, size=456) 6 print("test_sg") 7 return HttpResponse("OK")