TOC

CSRF简介

    CSRF也称XSRF,全称为Cross-site Request Forgery,它的意思是跨站请求伪造,CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如,NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI等;
    我们可以这么理解CSRF攻击,攻击者盗用了用户身份,以用户的名义发送恶意请求,从而,以用户的身份进行一些合法的操作,从而损害用户的利益,CSRF常见的攻击形式有很多,如以用户名义发送邮件、发消息、盗取用户的账号,甚至于购买商品、虚拟货币转账等;

CSRF详解

    如下图,这就是一个CSRF跨站攻击的整个流程图,中间的客户端,第一次访问并登陆了一个mybank.com的网站,登陆了之后,这个网站往往会发送一些Cookie回来,实现用户的状态保持,然后用户在没有关闭浏览器的情况下,又打开了一个域名为attacker.com的网站,并且打开了这个网站下面一个包含恶意代码的页面,这个页面的HTML文档内包含着攻击的代码,大概的意思,就是创建一个Form表单,向mybank.com发送转账请求;

    那么根据Cookie的原理,只要浏览器不关闭,就不会立刻失效,所以实际上,对于这个受害者来讲,向https://mybank.com/transfer.jsp网址发起POST请求使用的是自己本地浏览器的Cookie值,那么因为受害者在访问attacker.com网站之前登陆过,那么此时就出问题了,直接使用mybank.com颁发的已登陆的令牌,向mybank.com随意提交数据;
    虽然,我们是在攻击的网站发起的请求,但是请求的客户端还是本地浏览器,所以访问时附带的还是mybank.com相关的Cookie值,可以看到https://mybank.com/transfer.jsp大概就是一个转账的页面,参数也包含了收帐人和金额,如果说mybank.com这个网站的安全性做得不够强,那么这个操作带来的直接结果就是,受害者账户里面的钱就被转走了;
    可以看到,CSRF就是一种跨站伪造技术,所以Django对这个安全问题进行了处理,加入了一个CSRF校验中间件,一旦发生POST提交表单的请求,都需要校验它的scrf_token是否合法,从而保证整个服务端的安全;

解决方案

    在Django框架中,对于CSRF来讲,它的解决方案主要有三种,第一种不推荐使用,就是直接关闭CSRF Token的中间件,CSRF的产生主要就是为了解决网站的安全问题的,那我们将其关闭,对网站的安全性就极大的降低了,除非我们有其他的替代方案;
    第二种方式,需要借助模版语言才能完成,它是一种随机数的方式,主要的实现手段是在模版中加入一个{% csrf_token %}的模版标签来实现,一旦我们一个页面使用render函数进行模版渲染,Django会生成两个随机字符串,然后分别对这两个随机字符串使用SECRET_KEY作为salt进行加密;
    其中,第一个随机字符串加密后的密文会放在Cookie里面,键名为csrftoken,第二个随机字符串加密后的密文会在表单中新增一个隐藏的input标签,如<input type="hidden" name="csrfmiddlewaretoken" value="eaLcTa6xsceTpLgNtFQ3MCNxDKRfk5PE2PqluPwdFukPkPSN9RMubV74Hsi2teow">,这样只有浏览器在提交数据时,将这两个随机字符串发送给服务端,服务端拿到之后使用SECRET_KEY进行解密,如果成功,就说明这个请求是一个合法的请求;
    第三种方式,主要是针对AJAX请求的,针对AJAX请求来讲,我们需要手动的在From表单中添加csrfmiddlewaretoken信息,或者是在请求头中添加X-CSRFToken头部添加csrf_token的值,而csrf_token的值可以直接从返回的cookie中提取,服务端校验成功,才会真正的通过这一次POST表单提交请求;

中间件技术

    Django中间件实际上就是一个请求和响应的钩子框架,它是一个轻量级的且低级的插件系统,主要用于全局改变Django的输入和输出,这里需要注意的是它是全局生效,因此要谨慎使用,否则不仅会造成难以定位的错误,而且可能会导致整体性能低下;
    那么从底层原理去讲Django中间件,这里就涉及到Django的整个请求流程了,如下图,WSGI Server之后,WSGI Server会将请求封装成environ,然后转交给Django的WSGI App之后做的一些操作,那么当请求达到Django App之后,会经过四个流程,第一个流程就是中间件,当中间件处理完成之后会进入路由系统,路由系统匹配完成之后进入视图函数,这就是整个请求报文的流转流程;
    那么当视图函数处理完成之后,会封装响应报文,响应报文会一层一层的返回,视图函数处理完成之后,报文会返回到路由系统,然后返回到一个一个中间件,最后返回给WSGI Server,从而达到客户端,如下图;

内置中间件

    在Django框架中,中间件技术大量应用,比如我们熟知的Session、CSRF等等,在Django的settings.py配置文件可以看到一个MIDDLEWARE的列表,里面每个元素都是一个中间件,这些中间件"自上而下"依次运行,所以,开发者自定义的中间件,也需要加载到这个列表当中,从而将这个自定义中间件注册到当前Django系统中,如下;
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',
]
    如上,这七个中间件是Django框架中默认自带的中间件,它们"自上而下"执行,分别实现不同的功能,如下;
django.middleware.security.SecurityMiddleware
        做了一些安全处理的中间件。比如设置XSS防御的请求头,比如做了http协议转为https协议的工作等;
django.contrib.sessions.middleware.SessionMiddleware
        session中间件,该中间件会给request对象添加一个处理好的session对象;
django.middleware.common.CommonMiddleware
        通用中间件,处理URL,比如baidu.com会自动的处理成www.baidu.com,或/blog/1会处理成/blog/1/自动加上反斜杠;
django.middleware.csrf.CsrfViewMiddleware
        保护中间件,在提交表单的时候会必须加入csrf_token,cookie中也会生成一个名叫csrftoken的值,也会在header中加入一个HTTP_X_CSRFTOKEN的值来放置CSRF攻击,SessionMiddleware必须出现在CsrfMiddleware之前;
django.contrib.auth.middleware.AuthenticationMiddleware
        用户授权中间件,会给request添加一个user对象的中间件,该中间件必须在sessionmiddleware后面;
django.contrib.messages.middleware.MessageMiddleware
        该中间件为消息处理中间件,主要是为了在多个模板中可以使用我们返回给模板的变量,并且简化操作;
django.middleware.clickjacking.XFrameOptionsMiddleware
        该中间件主要用于防止通过浏览器页面跨Frame出现clickjacking(欺骗点击)攻击出现;

定义语法

    中间件的定义一共有两种定义方式,一种是函数的方式,该函数并非一个简单的函数,而是一个闭包函数,外层函数需要接收一个get_response的参数,内层函数接收一个request的参数,另一种方式,是使用类的方式,类的方式需要提供一个初始化方法,该方法,接收一个get_response的参数,然后该类里面,需要定义一个__call__的魔术方法,该方法接收一个request参数,函数的定义方式,在此不做赘述,我们主要查看类的定义方式,示例如下;
class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    def __call__(self, request):
        response = self.get_response(request)
        return response
    可以看到,该函数其实就是一个可调用函数,我们可以像普通函数调用那样去调用这个函数的实例对象,该实例对象里面有一个self.get_response(request)的函数,这个函数尤为重要,它主要实现两个钩子功能,第一个功能process_request,表示,在请求到达路由系统之前,需要对request请求对象做的一些处理,第二个功能是process_response,表示,当视图函数处理完成之后,响应报文通过路由系统返回到中间件时,中间件需要对响应报文做的一些处理,详情查看钩子函数说明;

钩子函数

    Django的中间件,有所谓的钩子函数的说法,在Django的1.11版本之前,Django总共有五个钩子函数,即process_request、process_response、process_view、process_exception和process_template_response,那么在Django1.11版本之后,取消了,process_request和process_response这两个钩子函数,先分别介绍下这几个钩子函数的主要用途吧,如下;
process_request(request):中间件第一个执行的钩子函数,但在Django 1.11版本后,它不再是一个函数,而是get_response(request)上面的代码段,在这个代码段中,我们可以对发送过来的请求报文,做一些预处理,当然,我们也可以直接响应客户端,需要注意的是,如果该代码段中存在return语句,并且返回值是一个HttpResponse对象,将不会继续往下执行,而是直接响应客户端;
process_view(request, view_func, view_args, view_kwargs):在执行完所有的process_request钩子函数之后,会执行该钩子函数,在该钩子函数中,我们可以拿到经过路由匹配到的视图处理函数,所以,该钩子方法,就是在正式进入视图处理函数之前,对request对象需要做的一些操作,如果该函数,返回值为None,则继续执行后面的中间件的process_view函数,如果返回HttpResponse,则不执行后续的process_view函数,也不执行视图函数,然后执行所有的process_response中间件;
process_exception(request, exception):该钩子函数为执行视图函数的过程中如果引发异常,则按照settings.py中MIDDLEWARE_CLASSES的顺序,倒序执行process_exception方法来处理,如果返回None,继续执行下一个中间件的process_exception方法,如果返回HttpReponse对象,则该中间件上方其他中间件的process_exception方法不会被调用,一旦其中某个中间件有返回值,则调用template_response和response中间件;
process_template_response(request, response):该钩子函数是在视图函数执行结束之后执行,但是该钩子函数的执行有一个条件,就是response实现了render方法才会执行,一旦所有的中间件的template_response被执行完,则调用render方法;
process_response(request, response):该钩子函数为视图函数处理完成之后,响应报文到达路由系统的下一站,该钩子函数必须有返回值,且返回类型必须是HttpResponse对象;
    在Django 1.11版本之后,虽然取消了,process_request和process_response这两个钩子函数,但是这两个钩子函数的功能,依旧存在,在Django 1.11版本后,process_request不再是一个钩子函数,而是在执行self.get_response(request)函数之前做的一些操作,我们都可以看成这就是process_request的实现,而在执行self.get_response(request)函数之后做的一些操作,我们可以看作process_response的实现,如下;
class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    def __call__(self, request):
        # 此处为process_request的代码段
        response = self.get_response(request)
        # 此处为process_response的代码段
        return response
    def process_view(self, request, view_func, view_args, view_kwargs):
        # 此处为process_view的代码段
    def process_exception(self, request, exception):
        # 此处为process_exception的代码段
    def process_template_response(self, request, response):
        # 此处为process_template_response的代码段
        return response

总结

    其实Django中间件,无非就是一个拦截器,一个在请求达到Djangp App之后处理,一个在Django达到路由系统之后处理,一个在请求达到视图函数时,在视图函数的处理过程中出现异常时处理,一个是在视图函数处理的使用了模版技术且在调用render函数之前处理,另一个则是在视图函数返回了一个HttpResponse之后处理;
    那么上述说了,process_request和process_response两个钩子,额外,我们可能会使用到process_view钩子函数,该钩子函数有点特殊,如果我们希望定义一个仅有process_view钩子函数的中间件,比如结合,process_request来实现,再就是注意一下process_view的返回值,具体的查看上述介绍,不在此做过多的赘述;
    此外,对于process_exception和process_template_response这两个钩子函数,在实际的应用场景用使用的并不多,所以不在此做过多的介绍,具体的请查看官方文档;

类视图

    函数视图有这么一些不足,如有时视图函数的内容就比较复杂,比如,使用多个条件分支判断请求方法将导致代码的可读性较差,同时函数视图的大量使用似乎过度发力于面向过程编程,并没有使用到构成框架的基础设施即Python语言的面向对象这一优秀的编程思想;
    然而,这些缺点却可以通过类视图(Class-Based View,CBV)来弥补,尽管类视图并不是函数视图的平替方案,但相比于函数视图,它有自己的优势,如与特定的HTTP方法(GET, POST, 等等)关联的代码组织能通过单独的类方法来替代,从而解决函数视图需要通过method判断来来进行多分支处理,同时,面向对象技术(比如MIXIN多重继承)可用于将代码分解为可重用组件,从而减少代码的冗余性;
    对于Django的类视图来讲,首先,我们需要编写一个继承自django.views.View的类,然后再在这个类,下面分别定义get、post、delete等方法,这些方法,都需要接收一个request参数,那么当我们的类视图定义完成之后,想要将这个类视图,和路由系统进行关联,我们需要调用这个类视图的as_view()方法,因为路由系统,对应的只能是一个路由处理函数,所以需要使用as_view()方法包装一下,如下;
from django.views import View
class home(View):
    def get(self,request):
        pass
    def post(self,request):
        pass
    def delete(self,request):
        pass
    def put(self,request):
        pass
# urls.py
from user.views import home
urlpatterns = [
    path('', home.as_view()), # 调用这个类的as_view()方法,拿到一个被包装的视图函数
]

源码解析

    上面说过,路由系统对应的必须是一个函数,所以从这一点来看,我们调用一个类视图的as_view()方法,就会拿到一个函数,那么具体怎么拿到一个函数呢,如下代码解析;
class View:
    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            setattr(self, key, value)

    @classonlymethod
    def as_view(cls, **initkwargs):
        for key in initkwargs:
            if key in cls.http_method_names:
                raise TypeError("You tried to pass in the %s method name as a "
                                "keyword argument to %s(). Don't do that."
                                % (key, cls.__name__))
            if not hasattr(cls, key):
                raise TypeError("%s() received an invalid keyword %r. as_view "
                                "only accepts arguments that are already "
                                "attributes of the class." % (cls.__name__, key))

        def view(request, *args, **kwargs):
            self = cls(**initkwargs)
            if hasattr(self, 'get') and not hasattr(self, 'head'):
                self.head = self.get
            self.setup(request, *args, **kwargs)
            if not hasattr(self, 'request'):
                raise AttributeError(
                    "%s instance has no 'request' attribute. Did you override "
                    "setup() and forget to call super()?" % cls.__name__
                )
            return self.dispatch(request, *args, **kwargs)
        view.view_class = cls
        view.view_initkwargs = initkwargs
        update_wrapper(view, cls, updated=())
        update_wrapper(view, cls.dispatch, assigned=())
        return view

    def setup(self, request, *args, **kwargs):
        self.request = request
        self.args = args
        self.kwargs = kwargs

    def dispatch(self, request, *args, **kwargs):
        if request.method.lower() in self.http_method_names:
            handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
        else:
            handler = self.http_method_not_allowed
        return handler(request, *args, **kwargs)

    def http_method_not_allowed(self, request, *args, **kwargs):
        logger.warning(
            'Method Not Allowed (%s): %s', request.method, request.path,
            extra={'status_code': 405, 'request': request}
        )
        return HttpResponseNotAllowed(self._allowed_methods())

    def options(self, request, *args, **kwargs):
        response = HttpResponse()
        response['Allow'] = ', '.join(self._allowed_methods())
        response['Content-Length'] = '0'
        return response

    def _allowed_methods(self):
        return [m.upper() for m in self.http_method_names if hasattr(self, m)]
    如果上就是整个django.views.View类的所有源代码,因为我们自定义类视图都会继承自这一个类,所以就继承了这个类下面的所有方法;
    那么,当一个请求过来时,路由系统会将这个请求转交给自定义类视图的as_view()方法,同时将request对象传入进去,即as_view(request),那么如果我们的路由中有做参数匹配,一样,会以键值对的方式传入进去,因此,as_view()函数,接收两个参数,第一个是cls,第二个是**initkwargs;
    第一个参数的cls,实际上就是我们的类视图本身,因为我们是直接调用类视图.as_view()方法,所以第一参为类视图,那么第二个参数**initkwargs,无需关心,那么当执行到as_view()函数之后,它的返回值,就是一个名为view的闭包函数,所以,从这里看,我们的路由确确实实是对应的一个函数,只不过对于类视图类将,它是一个闭包函数;
    该闭包函数,接收三个参数,view(request, *args, **kwargs),request为当前的请求对象,而args则是无名分组的匹配值,而kwargs则是有名分组的匹配值,然后该函数,首先会进行View类的初始化,从而,得到一个self,然后会执行一个self.setup()的方法,将我们的request对象和分组匹配值,添加到当前实例对象中,以备下一步调用;
    最后这个闭包函数,返回一个self.dispatch()的方法,该方法,实际上就是从视图类中,提取对应request.method对应的方法,然后执行,并将结果返回,这就是整个类视图的原理;

总结

    从上述的源码分析结果来看,实际上,所谓类视图,只不过是将一个类里面的方法提取出来,作为一个函数交给路由系统而已,本质上还是一条路由对应一个函数,只不过使用类视图让我们的代码更加简洁明了,更符合面向对象的设计思想;

装饰器

    在装饰器这里,主要是讨论如何在类视图的情况下使用装饰器,函数视图装饰器和之前没什么不一样,但是在Django的类视图下面,我们想要使用装饰器,可能有点麻烦,这个装饰器,我们需要经过特殊改造,其主要原因是因为上述类视图的django.views.View下面的第47行代码,在一个类里面调用一个方法,Python解释器会将self作为自己参数,所以我们的装饰器内层函数,应该最少两个参数,第一个接收self(View类的实例),第二参数为request,验证如下;
class cce:
    def __init__(self):
        self.value = 1

    def c1(self):
        cce = getattr(self, 'c2')
        return cce() # 返回cce函数的执行结果

    def c2(*args):
        return args


c1 = cce()
print(c1.c1()) # (<__main__.cce object at 0x102f19c18>,) 
# 可以看到,默认在没有传任何参数的情况下,Python解释器会加入一个self参数;
    那么为了解决这个问题,我们主要有两种解决方案,第一种,使用django.utils.decorators类下面的method_decorator方法进行修饰,第二种修改我们的自定义装饰器内层函数,内层函数最少接收两个参数;

method_decorator

    method_decorator的作用是为函数视图装饰器补充第一个self参数,以适配类视图方法,它其实就是一个带参数的装饰器,它的使用方法有两种,第一种直接在类上面加入这个装饰器,然后将自定义装饰器,和需要加装饰器的method方法传进去即可,第二种是直接在类视图里面对应的method上面加入这个装饰器,并将自定义装饰器以参数的方式传入,如下;
# 方式一
@method_decorator(authenticate,name="get")
class index(View):
    def get(self, request):
        return JsonResponse({"message": '欢迎!!!'})
# 方式二
class index(View):
    @method_decorator(authenticate)
    def get(self, request):
        return JsonResponse({"message": '欢迎!!!'})

修改自定义装饰器

    那么还有一种实现方案,就是在自定义装饰器里面,加入一个参数用来接收View类的实例,所以对于自定义装饰器来讲,我们最少接收两个参数,一个为View类的实例,一个为request对象,那么为了解决路由命名分组和路由无名分组的问题,所以,我们应该使用*args和**kwargs来匹配其他的参数,如下;
def decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

class index(View):
    @decorator
    def get(self, request):
        return JsonResponse({"message": '欢迎!!!'})

Cookie-Session

    Cookie是由网景公司发明的,因为HTTP是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息),每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一客户端,如果每次请求都经过再次确认的话毫无疑问会不必要地消耗很多服务器的资源。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问自己),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器,而这个状态的管理需要借助cookie和session来实现;
    Cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie使基于无状态的HTTP协议记录用户的状态信息成为了可能;
    那么由于Cookie带来的各种问题,比如数据安全问题、最大长度问题、以及服务端无法验证数据是否被篡改的问题,因此Session出现了,Session也是一种状态保存技术,Session的实现需要借助Cookie,服务端开启Session之后,会创建一个键值对儿,这个值存储在服务端本地,默认在内存中,我们也可以选择存在指定的数据库中;
    那么这个键,会作为一个值,放在cookie中的sessionid内,那么当这个Cookie以Response的形式返回给客户端之后,根据Cookie的特性,当客户端下一次访问,会携带着这个Cookie,那么服务端就可以在这个Cookie里面提取出sessionid的值,从而去指定的数据库中检索这个值对应的value,从而实现状态的记录;
    那么既然两者都是解决无状态的问题,有了Cookie为什么还要有Session呢,这个问题分多方面看,第一方面,数据存储,Cookie的数据存储是浏览器,而Session的数据存储是服务端,存储在客户端的数据非常不安全,很容易被篡改,即使被篡改了;
    第二方面是,数据大小的问题,Cookie最多只能存储4K大小的数据,而Session不一样,Session没有任何容量的限制,第三方面,还是安全方面,Cookie是明文存储的,我们可以在浏览器上很直观的看到,Cookie内部的详细信息,并且对于一个设定了过期时间的Cookie来讲,它是或持久化到磁盘的,给网站的安全带来了极大的问题;
    而Session就不同了,虽然Session是借助Cookie来实现的,但是,Session只会借助Cookie存储一个随机字符串,这个随机字符串无任何意义,数据是存在服务端层面的,随机字符串只是一个查找数据的键而已,极大的扩展了数据的存储容量和安全性;

Session配置

    Session作为一个广泛使用的会话保持方案,市面上各大Web框架都有支持,Django当然也在其内,在Django内,如果我们希望使用Session,首先,我们需要将Session的App加入到Django配置的INSTALLED_APPS当中,即django.contrib.sessions,然后将Session的中间件MIDDLEWARE配置当中,即django.contrib.sessions.middleware.SessionMiddleware。那么对于Session的常用配置有很多,如下;
# Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
SESSION_COOKIE_NAME = 'sessionid'
# Session的cookie失效日期(2周)(默认)
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
# Session的cookie保存的域名(默认)
SESSION_COOKIE_DOMAIN = None
# 是否Https传输cookie(默认)
SESSION_COOKIE_SECURE = False
# Session的cookie保存的路径(默认)
SESSION_COOKIE_PATH = '/'
# 是否Session的cookie只支持http传输(默认)
SESSION_COOKIE_HTTPONLY = True
# 是否每次请求都保存Session,默认修改之后才保存(默认)
SESSION_SAVE_EVERY_REQUEST = False
# 是否关闭浏览器使得Session过期(默认)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
# 存储session数据默认使用的模块
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
# session数据的序列化类
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
    随后我们需要先解决Session的存储问题,Session默认会存储在数据库中,在我们创建一个Django框架之后,使用migrate创建初始化数据库,会在数据库中创建一个django_session的表,这个表,默认就是用来存储session的键值对儿的地方,如下;

    那么如果我们希望将session存储在其他内存型数据库,也非常简单,我们只需要在Django里面加入CACHE的配置项,然后设置一下SESSION的存储配置即可,如下;
CACHES = {
    "default": {#default 必须加上,不然会报错
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/5",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 10000},
            "PASSWORD": "caidaye",
        }
    }
}
# session的存储配置
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'  # 设置缓存机制为CACHES
SESSION_CACHE_ALIAS = 'default'  # CACHES的redis

Session常用方法

    那么如果在Django中去操作Session,也很简单,我们的request对象中,就有一个session属性,该属性对应Session对象,我们可以在这个对象上面添加、获取、删除Session,常用方法如下;
set_expiry:对当前session设置过期时间;
keys:拿到所有session的session_key;
values:拿到所有session的value;
items:拿到所有session键值对儿;
get:获取指定session_key的value;
clear_expired:删除所有已过期的session;
exists:判断指定的session_key是否存在;
delete:删除指定session_key对应的值;
clear:删除当前这个请求对应的session数据;

Django项目部署

    Django项目的部署,最简单的方式,我们可以将整个项目打包放在服务器上跑起来也行,然后将nginx指向Django的WSGI端口就行了,那么还有一种比较常见的方式,就是将整个Django项目进行打包,针对Django项目的打包,主要是使用一个setup的内置函数,那么在打包之前,我们需要着重注意Django项目的几个配置,比如DEBUG、ALLOWED_HOSTS和LOGGING,在打包之前一定要配置好;
    使用setup内置函数打包Django项目需要在Django项目的根路径下面创建一个setup.py的文件,然后在里面加入打包相应的配置,即可;
from distutils.core import setup
import glob

setup(
    name="blog",
    version="0.0.1",
    author="cce",
    author_email="mail0426@163.com",
    url="blog.doorta.com",
    description="A simple blog site",
    packages=["blog", "user", "utils"],
    py_modules=["manage"],
    data_files=glob.glob("user/templates/*.html") + ["requirements.txt"],
)

# python3.5 setup.py sdist

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注