Django实现表单验证、CSRF、cookie和session、缓存、数据库多表操作(双下划綫)
通常验证用户输入是否合法的话,是前端js和后端共同验证的,这是因为前端js是可以被禁用的,假如被禁用了,那就没法用js实现验证合法与否了,也就是即使用户输入的不合法,但是也没提示,用户也不知道怎么输入就合法了。
所以下面会讲到在django后台实现验证是否合法并将错误提示显示给用户。
1.django实现form表单验证
使用django内置功能实现用户输入信息的验证
新建一个project,比如叫django1217;
新建一个app,python manage.py startapp app01
settings,py,
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01',#注册了ap01 ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', #注释这行 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] #设置静态资源路径 STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR,'static'), )
urls.py,
from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.login), ]
app01→views.py,
from django.shortcuts import render # Create your views here. from django import forms #定义各个输入框的规则,比如下面的不允许user和pwd为空 class LoginForm(forms.Form): #字段的值比如跟html中该标签的name值一样 #比如这里的user对应html里<input type="text" name="user" />中的name值。 user = forms.CharField(required=True) pwd = forms.CharField(required=True) def login(request): if request.method == 'POST': # u = request.POST.get('user') # p = request.POST.get('pwd') # print(u,p) obj = LoginForm(request.POST) #如果所有规则都满足,则ret为true,只要有一条不满足,ret就为false。 ret = obj.is_valid() #obj.clean()就是获取到的用户输入信息 if ret: print(obj.clean()) else: #obj.errors,获取错误信息。 print(obj.errors) return render(request,'login.html')
templates→login.html,
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <input type="text" name="user" /> </div> <div> <input type="password" name="pwd" /> </div> <input type="button" value="login" onclick="OnSubmit()"/> <script src="/static/js/jquery-1.12.4.js" ></script> <script> function OnSubmit(){ var info_dict = {}; $('input').each(function(){ #将name值作为键,value值作为值,加入到字典。 var v = $(this).val(); var n = $(this).attr('name'); info_dict[n] = v; }); $.ajax({ url : '/login/', type : 'POST', data : info_dict , success:function(data){ }, error:function(data){ } }) } </script> </body> </html>
如果用户名密码都为空时点登陆,则obj.errors信息见下:
<ul class="errorlist"><li>pwd<ul class="errorlist"><li>This field is required.</li></ul></li><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul> #提示用户名密码都是被要求的
如果用户名为空,密码不为空时点登陆,则obj.errors信息见下:
<ul class="errorlist"><li>user<ul class="errorlist"><li>This field is required.</li></ul></li></ul> #提示用户名是被要求的
如果都不为空,则obj.clean()信息见下:
{'pwd': '123', 'user': '123'} #都不为空,也就不报错了,直接获取到用户输入的内容。
可见,当验证不通过时,obj.errors会保存一段html代码,默认是保存为html代码,也可以保存为其他类型,
obj.errors.as_data() obj.errors.as_json() #保存为json格式 obj.errors.as_ul() #默认的 obj.errors.as_text()
2.在上面代码的基础上进行功能扩展
login.html,
<script src="/static/js/jquery-1.12.4.js" ></script> <script> function OnSubmit(){ $('.input-warning').remove(); var info_dict = {}; $('input').each(function(){ var v = $(this).val(); var n = $(this).attr('name'); info_dict[n] = v; }); $.ajax({ url : '/login/', type : 'POST', data : info_dict , dataType : 'json', success:function(data){ #如果返回真,就跳转;返回假,就在html页面提示报警信息。 if(data['status']) { location.href = 'https://www.baidu.com' } else{ $.each(data.message,function(k,v){ error_message = v[0].message; var tag = document.createElement('span'); tag.className = 'input-warning'; tag.innerText = error_message; $('input[name="' + k + '"]').after(tag); })} }, error:function(data){ } }) } </script>
views.py,
from django.shortcuts import render from django.shortcuts import HttpResponse # Create your views here. from django import forms class LoginForm(forms.Form): user = forms.CharField(required=True) pwd = forms.CharField(required=True) import json def login(request): if request.method == 'POST': result = {'status':False,'message':''} obj = LoginForm(request.POST) ret = obj.is_valid() if ret: result['status'] = True result['message'] = json.loads(obj.errors.as_json()) else: from django.forms.utils import ErrorDict #obj.errors.as_json()本身就是json格式,所以没法再json.dumps,所以必须先json.loads,然后return HttpResponse(json.dumps(result))才不会报错。 result['message'] = json.loads(obj.errors.as_json()) return HttpResponse(json.dumps(result)) return render(request,'login.html')
3.在上面的基础上继续扩展
上面的提示信息都是英文的,下面演示如何自定义,
login.html新加两个input,
<div> <input type="text" name="number" /> </div> <div> <input type="text" name="phone" /> </div>
views.py修改如下,
from django import forms import re 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('手机号码格式错误') #“required=True”是默认就存在的,写不写都行; #validators,使用自定义的函数来校验是否合法。 class LoginForm(forms.Form): user = forms.CharField(required=True,error_messages={'required':'用户名不能为空'}) pwd = forms.CharField(min_length=6,max_length=15,error_messages={'required':'不能为空','min_length':'最小长度是6','max_length':'最大长度是15'}) number = forms.IntegerField(error_messages={'invalid':'必须是数字','required':'不能为空'}) phone = forms.CharField(validators=[mobile_validate, ], error_messages={'required': u'手机不能为空'},)
4.使用html的form表单实现提交、使用django自动生成html标签
之所讲用ajax实现表单提交,有个很重要的因素是让用户输入的内容能保留;如果用form提交的话,会强制页面刷新,用户输入的东西会丢掉。下面讲解用form提交实现内容保留。
views.py,
from django.shortcuts import render from django.shortcuts import HttpResponse # Create your views here. from django import forms import re 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 LoginForm(forms.Form): user = forms.CharField(required=True,error_messages={'required':'用户名不能为空'}) pwd = forms.CharField(min_length=6,max_length=15,error_messages={'required':'不能为空','min_length':'最小长度是6','max_length':'最大长度是15'}) number = forms.IntegerField(error_messages={'invalid':'必须是数字','required':'不能为空'}) phone = forms.CharField(validators=[mobile_validate, ], error_messages={'required': u'手机不能为空'},) import json def login(request): if request.method == 'POST': objPost = LoginForm(request.POST) #如果是POST方法,此时的objPost里的输入框其实是有用户输入的数据的,所以将objPost返回时,也就将用户的输入信息一并返回了。 return render(request,'login.html',{'obj':objPost}) else: objGet = LoginForm() #如果是get方法,则直接将页面返回,也就是说所有的输入框都是空白的。 return render(request,'login.html',{'obj':objGet})
login.html,
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> .input-warning{ color: #dc44cb; } </style> </head> <body> <form action='/login/' method="post"> <div> #引用一个obj.user,django就会在html里自动创建一个input标签 {{ obj.user }} #如果有错误信息,才添加span标签并显示警告 {% if obj.errors.user %} <span class="input-warning">{{ obj.errors.user.0 }}</span> {% endif %} </div> <div> {{ obj.pwd }} {% if obj.errors.pwd %} <span class="input-warning">{{ obj.errors.pwd.0 }}</span> {% endif %} </div> <div> {{ obj.number }} {% if obj.errors.number %} <span class="input-warning">{{ obj.errors.number.0 }}</span> {% endif %} </div> <div> {{ obj.phone }} {% if obj.errors.phone %} <span class="input-warning">{{ obj.errors.phone.0 }}</span> {% endif %} </div> <input type="submit" value="login"/> </form> </body> </html>
注意,django版本不同,默认创建的标签可能不一样。比如number = forms.IntegerField(),在django1.9里自动生成一个和input框一模一样的框,在django1.10里会生成一个针对数字的框,这样的,
CharField()默认生成input标签,生成什么标签是可以指定的,也可以添加class、value等参数,见下,
#生成一个文本框标签,添加一个class和一个kkk3属性。 test = forms.CharField(widge=forms.Textarea(attrs={'class':'input-warning','kkk3':'iamgood'})) #生成一个select标签 select_choices = ( (0,'上海'), (1,'北京'), ) test = forms.CharField(widge=forms.Select(choices=select_choices))
5.CSRF介绍和使用
csrf是跨站域名伪造的缩写,如果django的settings.py里开启了这个,则form表单提交或ajax提交时必须携带着token(一段字符串,就当是密钥吧),否则会返回403拒绝错误。
django为用户实现防止跨站请求伪造的功能,通过中间件 django.middleware.csrf.CsrfViewMiddleware 来完成。而对于django中设置防跨站请求伪造功能有分为全局和局部。
全局:
中间件 django.middleware.csrf.CsrfViewMiddleware
局部:
- @csrf_protect,为当前函数(views.py里)强制设置防跨站请求伪造功能,即便settings中没有设置全局中间件。
- @csrf_exempt,取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件。
注:from django.views.decorators.csrf import csrf_exempt,csrf_protect
5.1 form表单提交配置csrf措施
urls.py,
url(r'^csrf/', views.csrf),
settings.py,
MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', #打开这一项 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ]
csrf.html,
<body> <form action="/csrf/" method="post"> {% csrf_token %}#加上这一行,就能使csrf不拒绝了。 <input type="text" name="user" /> <input type="password" name="pwd" /> <input type="submit" value="submit"/> </form> </body>
views.py,
def csrf(request): return render(request,'csrf.html')
5.2 ajax提交配置csrf措施
csrf.html,
<body> <input type="button" onclick="Do();" value="Do it"/> <script src="/static/js/jquery-1.12.4.js"></script> <script src="/static/js/jquery.cookie.js"></script> <script type="text/javascript"> var csrftoken = $.cookie('csrftoken'); function csrfSafeMethod(method) { // these HTTP methods do not require CSRF protection return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } #一个html页面可能有多个ajax请求,比如有登录的有注册的,下面这几行代码的作用就是给每个ajax请求都绑定了csrf功能,不然的话,需要在每个ajax请求里单独配置csrf功能。 $.ajaxSetup({ beforeSend: function(xhr, settings) { if (!csrfSafeMethod(settings.type) && !this.crossDomain) { xhr.setRequestHeader("X-CSRFToken", csrftoken); } } }); function Do(){ $.ajax({ url:"/csrf/", data:{id:1}, type:'POST', success:function(data){ console.log(data); } }); } </script> </body>
6.Cookie和Session介绍
Cookie保存在客户端的某个文件夹内;Session保存在服务端的数据库、memcached或/dev/shm等设备中。cookie是一个单独存在的东西,session依赖cookie。
用户进入网站登录后,浏览器会在本地写一个标识(可以是一个代号或者是一串字符串),下次用户再访问网站时,如果cookie里有内容并内容与session里的该用户的内容对应上了,这个用户就是登录状态了。
6.1 返回页面的同时往客户端写入cookie
def cookie(request): obj = render(request,'cookie.html') #k1=v1 obj.set_cookie('k1','v1') return obj
按照上面的代码,所有访问服务端的客户端都会写上一个k1、v1的cookie。
set_cookie函数:
def set_cookie(self,key,value='',max_age=None,expires=None,path='/',domain=None,secure=False,httponly=False): #key,键 #value,值 #max_age,这个cookie存活多少秒 #expires,指定一个过期时间 #path,‘/’这个表示cookie全域名有效。'/dotest'这个表示该cookie只在/dotest域名下有效,访问别的域名的话取不到这个cookie。 #domain=None,表示不允许跨域名有效,比如jd.com下的cookie不能用在taobao.com里。 #secure和httponly都是安全设置,但是设置了也不是百分百安全。 常用的就是key,value,max_age,expires,path。
cookie一般保存一些不敏感的信息,比如临时购物车商品、用户选择的一些属性(最大显示多少条信息,50条还是100条)。
像用户登录验证的信息,一般是服务端发给客户端一段随机码,cookie只保存这段随机码,下次访问网站的时候,把这随机码发给服务端(客户端不需要配置什么,浏览器会自动把cookie发给服务端),然后服务端拿这个随机码与sesion比对,如果对应上就允许访问内容,如果对应不上就跳转到登录界面。
验证的小例子:
def index(request): username = request.COOKIES.get('username') if username: return render(request,'index.html',{'username':username}) else: #如果在cookie里取不到用户名,就跳转到登录。 return redirect('/login/')
7. Session介绍以及使用
flask、tornado默认都没有提供session功能,要么用cookie实现验证,要么自己写一个session验证;django默认就提供session功能。
7.1 django实现session示例
urls.py,
url(r'^session_login/', views.session_login), url(r'^session_index/', views.session_index),
session_index.html,
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>hi,{{ user }}welcome here.</h1> </body> </html>
session_login.html,
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> </head> <body> <form action='/session_login/' method="post"> <div> <input type="text" name="user"/> </div> <div> <input type="password" name="pwd"/> </div> <input type="submit" value="login"/> </form> </body> </html>
views.py,
def session_login(request): if request.method == 'POST': u = request.POST.get('user') p = request.POST.get('pwd') if u == 'z843248880' and p == '123': #如果用户名密码验证通过,这里做了两件事,第一,页面跳转到session_index;第二,保存session;request.session['user'] = u这一步就是django提供的session保存功能,它实际做了两件事,生成一个随机码放到本地,然后将这个随机码写入到浏览器的cookie里,下次浏览器访问会将cookie里的这个随机码发送给服务端比对。 request.session['user'] = u return redirect('/session_index') return render(request,'session_login.html') def session_index(request): #获取session里的key是user的值 user = request.session.get('user',None) if not user: return redirect('/session_login/') else: return render(request,'session_index.html',{'user':user})
django的session可以保存在数据库、缓存、文件里,默认是保存在数据库的django_session表里,如果是新建的app,默认是没有表的,需要“python manage.py makemigrations;python manage.py migrate”生成默认表,这些表中就包含一个django_session表,这个表按如下的规则存储session:
session_id | value | expires |
l7ckctaz0omo4q3gpze4k5ob5f8eemud | user:z8432488880 | 两周(默认是两周的过期时间) |
7.2 创建验证session的装饰器
上面的代码,
def session_index(request): user = request.session.get('user',None) if not user: return redirect('/session_login/') else: return render(request,'session_index.html',{'user':user})
可见,如果所有的页面都需要做session验证,则每个函数里都需要做一个“if not user”的判断,所以我们把验证的这个功能放到装饰器里,代码见下,
def auth(func): def inner(request,*args,**kwargs): user = request.session.get('user',None) if not user: return redirect('/session_login/') return func(request,*args,**kwargs) return inner @auth def session_index(request): user = request.session.get('user') return render(request,'session_index.html',{'user':user})
7.3 添加注销功能
注销其实就是删除用户的session,然后跳转到登录页。用户的session保存在数据库里,只要删除这个用户的session即可,代码见下,
views.py,
@auth def session_logout(request): #删除session del request.session['user'] return redirect('/session_login/')
session_index.html,
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>hi,{{ user }}welcome here.</h1> <a href="/session_logout">logout</a> </body> </html>
8.Session更多配置
django仅支持memcache作为缓存,如果要用redis,需要装第三方插件。
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:
- 数据库(默认)
- 缓存
- 文件
- 缓存+数据库
- 加密cookie
8.1 数据库Session
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认) b. 使用(不论以什么方式保存session,“使用”都是没有区别的,也就是即使更换了存session的方法,也不用改业务代码,只需要改一下配置就行了) def index(request): # 获取、设置、删除Session中数据 request.session['k1'] #获取 request.session.get('k1',None) #获取 request.session['k1'] = 123 #设置 request.session.setdefault('k1',123) # 如果k1有值则不设置 del request.session['k1'] #删除 # 所有 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用户session的随机字符串 request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete("session_key")
8.2 缓存session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存(下面有讲到缓存)的设置 SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存 b. 使用 同数据库session
比如使用Memcache缓存(python-memcached模块)作为缓存,则settings.py如下配置:
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置 # 此缓存使用python-memcached模块连接memcache;下面三个CACHES选择其一 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': 'unix:/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } } SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存
8.3 文件session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir() # 如:/var/folders/d3/j9tj0gz93dg06bmwxmhh6_xm0000gn/T SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串 SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径 SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名 SESSION_COOKIE_SECURE = False # 是否Https传输cookie SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输 SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期 SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存 b. 使用 同数据库session
8.4 缓存加数据库session
数据库用于做持久化,缓存用于提高效率;如果缓存有就走缓存,缓存没有就从数据库取数据,再将数据写到缓存。 a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎 # 这里也需要配置缓存,比如用Memcache,就写上Memcache的缓存配置。 b. 使用 同上
8.5 加密session
a. 配置 settings.py SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎 b. 使用 同上
9.缓存
由于Django是动态网站,所有每次请求均会去数据进行相应的操作,当程序访问量大时,耗时必然会更加明显,最简单解决方式是使用:缓存,缓存将一个某个views的返回值保存至内存或者memcache中,5分钟内再有人来访问时,则不再去执行view中的操作,而是直接从内存或者Redis中之前缓存的内容拿到,并返回。
Django中提供了6种缓存方式:
- 开发调试
- 内存
- 文件
- 数据库
- Memcache缓存(python-memcached模块)
- Memcache缓存(pylibmc模块)
9.1 配置
9.1.1 开发调试
# 此为开始调试用,实际内部不做任何操作 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.dummy.DummyCache', # 引擎 'TIMEOUT': 300, # 缓存超时时间(默认300,None表示永不过期,0表示立即过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存个数(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) }, 'KEY_PREFIX': '', # 缓存key的前缀(默认空) 'VERSION': 1, # 缓存key的版本(默认1) 'KEY_FUNCTION' 函数名 # 生成key的函数(默认函数会生成为:【前缀:版本:key】) } } # 自定义key def default_key_func(key, key_prefix, version): """ Default function to generate keys. Constructs the key used by all other methods. By default it prepends the `key_prefix'. KEY_FUNCTION can be used to specify an alternate function with custom key making behavior. """ return '%s:%s:%s' % (key_prefix, version, key) def get_key_func(key_func): """ Function to decide which key function to use. Defaults to ``default_key_func``. """ if key_func is not None: if callable(key_func): return key_func else: return import_string(key_func) return default_key_func
9.1.2 内存
# 此缓存将内容保存至内存的变量中 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', 'LOCATION': 'unique-snowflake',#'unique-snowflake'是一个名字,可以随便起,缓存放在内存里是以字典形式存在,这个名字就是字典的名字。 } } # 注:其他配置同开发调试版本
9.1.3 文件
# 此缓存将内容保存至文件 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 'LOCATION': '/var/tmp/django_cache', } } # 注:其他配置同开发调试版本
9.1.4 数据库
# 此缓存将内容保存至数据库 # 配置: CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', # 数据库表名 } } # 注:执行创建表命令 python manage.py createcachetable,执行这条命令后,django就会再额外创建一张 'my_cache_table'的表用来放置缓存数据。
9.1.5 Memcache缓存(python-memcached模块)
# 此缓存使用python-memcached模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': 'unix:/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': [ #配置多个memcache服务器,memcache天生支持集群 '172.19.26.240:11211', '172.19.26.242:11211', ] } }
9.1.6 Memcache缓存(pylibmc模块)
# 此缓存使用pylibmc模块连接memcache CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '127.0.0.1:11211', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': '/tmp/memcached.sock', } } CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyLibMCCache', 'LOCATION': [ '172.19.26.240:11211', '172.19.26.242:11211', ] } }
9.2 应用
9.2.1 全站使用
使用中间件,经过一系列的认证等操作,如果内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户,当返回给用户之前,判断缓存中是否已经存在,如果不存在则UpdateCacheMiddleware会将缓存保存至缓存,从而实现全站缓存 MIDDLEWARE = [ 'django.middleware.cache.UpdateCacheMiddleware', # 其他中间件... 'django.middleware.cache.FetchFromCacheMiddleware', ] #把django.middleware.cache.FetchFromCacheMiddleware放在最后的分析: #用户来访问网站,应该是满足了所有中间件的规则后,才将缓存(FetchFromCacheMiddleware)返回给用户,而不是不经过任何中间件就直接拿到缓存数据,所以把FetchFromCacheMiddleware放在最后。 #django.middleware.cache.UpdateCacheMiddleware放在第一的分析: #UpdateCacheMiddleware的作用是如果没有缓存,则将数据写入到缓存,所以应该是执行了所有中间件的process_response方法,然后再执行UpdateCacheMiddleware这个中间件,不然,比如把UpdateCacheMiddleware放在了中间, #那缓存之后,又有中间件的process_response方法里执行了添加数据的操作,则新添加的数据就没在缓存里面。 CACHE_MIDDLEWARE_ALIAS = "" CACHE_MIDDLEWARE_SECONDS = "" CACHE_MIDDLEWARE_KEY_PREFIX = ""
9.2.2 单独视图(页面)缓存
方式一: from django.views.decorators.cache import cache_page #60*15是超时时间,这个设置优先,单位是秒,60*15就是15分钟 @cache_page(60 * 15) def my_view(request): ... 方式二: from django.views.decorators.cache import cache_page urlpatterns = [ url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)), ]
9.2.3 页面局部使用
a. 引入TemplateTag {% load cache %} b. 使用缓存 {% cache 5000 缓存key %} 缓存内容 {% endcache %}
页面局部使用示例:
{% load cache %} <!DOCTYPE html> <html lang="en"> <head> <link rel="shortcut icon" href="123.ico"> <title name="ssss">ooo</title> <meta charset="UTF-8"> </head> <body> <h1>{{ c }}</h1>#c是返回的实时time <div style="border:1px solid red;height:100px;"> {% cache 5 缓存key %} {{ c }} #这块被缓存住了,所以5秒之内时间值是不对的。 {% endcache %} </div> </body> </html>
10. Model一对多操作补充(一)
model操作补充:
a.基本操作
b.进阶
c.双下划綫
__,比较大小与操作
__,可以跨表
10.1 all()、values('user')、value_list('user')分别存储的什么
#创建一个UserInfo表
class UserInfo(models.Model):
user = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
queryset = UserInfo.objects.all(),这条命令会获取该表的所有对象数据,赋值给queryset,
也就是说queryset里存储的是【UserInfo对象1,UserInfo对象2,UserInfo对象3,】
queryset = UserInfo.objects.all().values('user')
queryset里存储的是字典,【{'user':'zsc','user':'zsc1'}】
queryset = UserInfo.objects.all().value_list('user')
queryset里存储的是元组,直接存储的user的值,【('zsc'),('zsc1')】
10.2 直接用id设置外键
#创建一对多关系表 class UserType(models.Model): caption = models.CharField(max_length=32) #用户类型包括超级用户、普通用户、游客 class UserInfo(models.Model): user = models.CharField(max_length=32) pwd = models.CharField(max_length=32) user_type = models.ForeignKey('UserType') 创建一条UserInfo记录: 方法一: UserInfo.objects.create(user='zsc',pwd='zsv',user_type=UserType.objects.get(id=2));这样创建的话会操作两次数据库,效率低。 方法二: UserInfo这个表有外键,所以这个表有一列数据是“user_type_id”,这个“user_type_id”就对应着UserType里的数据。 UserInfo.objects.create(user='zsc',pwd='zsv',user_type_id=2)
10.3 利用双下划线跨表查询数据
10.3.1 仅在filter里使用双下划綫
#创建一对多关系表 class UserType(models.Model): caption = models.CharField(max_length=32) #用户类型包括超级用户、普通用户、游客 class UserInfo(models.Model): user = models.CharField(max_length=32) pwd = models.CharField(max_length=32) user_type = models.ForeignKey('UserType') #单表查询:UserInfo.objects.filter(user='zsc') #联表查询: 现在要查询所有用户类型是“普通用户”的所有用户名和密码 方法一: uid = UserType.models.get(caption='普通用户').id UserInfo.objects.filter(user_type_id=uid) #这样做可以,但还是进行了两次数据库操作。 方法二: UserInfo.objects.filter(user_type__caption='普通用户') #用双下划綫实现了跨表查询数据
10.3.2 在values里使用双下划线
queryset = UserInfo.objects.filter(user_type__caption='普通用户').values('user') #结果是:【{'user':'zsc1'},{'user':'zsc2'}】 queryset = UserInfo.objects.filter(user_type__caption='普通用户').values('user','user_type__caption') #结果是:【{'user':'zsc1','user_type__caption':'普通用户'},{'user':'zsc2','user_type__caption':'普通用户'}】,可见这样就直接把User_Type里的数据直接拿过来显示了。
10.3.3 使用双下划线实现三张及以上表的数据联合
#创建一对多关系表 class some(models.Model): name = models.CharField(max_length=32) class UserType(models.Model): caption = models.CharField(max_length=32) #用户类型包括超级用户、普通用户、游客 something = models.ForeignKey('some') class UserInfo(models.Model): user = models.CharField(max_length=32) pwd = models.CharField(max_length=32) user_type = models.ForeignKey('UserType') 现在要查询所有用户name是“旺旺”的所有用户名和密码,name字段在some表里。 UserInfo.objects.filter(user_type__something__name='旺旺')