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='旺旺')

 

posted @ 2016-12-18 09:55  freedom_dog  阅读(1447)  评论(0编辑  收藏  举报