Django models操作、中间件、缓存、信号、分页

什么是中间件?中间件应用场景?和装饰器区别?

中间件
对于所有批量的请求用中间件合适,而对于某些请求个数少用装饰器合适
- 中间件是什么?
- 是一个类
- 方法名必须是process_request/process_response,否则无效
- 返回值注意:
# 无返回值:继续执行后续中间件和视图函数
# 有返回值:执行自己的process_response和上面的response
- 做过什么:
- 用户登录验证 - csrf
- session
- 权限管理***

 

下边的图就是一次完整的django生命周期,从客户端输入url,经过wsgi模块处理,得到符合HTTP协议的字符串,走中间件,假如中间件return None,继续往下走到urls......;

假如中间件return Httpresponse/render/redirect  ...直接返回给用户,如果是httpresponse则展示给用户,其它两个的话继续走中间件:

 

在http请求 到达视图函数之前   和视图函数return之后,django会根据自己的规则在合适的时机执行中间件中相应的方法。

中间件的执行流程

1、执行完所有的request方法 到达视图函数。

2、执行中间件的其他方法

3、经过所有response方法 返回客户端。

注意:如果在其中1个中间件里 request方法里 return了值,就会执行当前中间件的response方法,返回给用户 然后 报错。。不会再执行下一个中间件。

1.在project下随便创建一个py文件

from django.utils.deprecation import MiddlewareMixin
class Middle1(MiddlewareMixin):
    def process_request(self,request):
        print("来了")
    def process_response(self, request,response):
        print('走了')

2、在setings文件中 注册这个 py文件

django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件

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',
    'M1.Middle1',
]

 执行结果

为啥报错了呢?

因为 自定义的中间件response方法没有return,交给下一个中间件,导致http请求中断了!!!

注意 自定义的中间件request 方法不要return  因为返回值中间件不再往下执行,导致 http请求到达不了视图层,因为request在视图之前执行!

from django.utils.deprecation import MiddlewareMixin
class Middle1(MiddlewareMixin):
    def process_request(self,request):
        print("来了") #不用return Django内部自动帮我们传递
    def process_response(self, request,response):
        print('走了')
        return response #执行完了这个中间件一定要 传递给下一个中间件

中间件(类)中5种方法

中间件中可以定义5个方法,分别是:

  • process_request(self,request)
  • process_view(self, request, callback, callback_args, callback_kwargs)
  • process_template_response(self,request,response)
  • process_exception(self, request, exception)
  • process_response(self, request, response

1、 process_view(self, request, callback, callback_args, callback_kwargs)方法介绍

(1)执行完所有中间件的request方法‘

(2)url匹配成功

(3)拿到 视图函数的名称、参数,(注意不执行) 再执行process_view()方法

(4)最后去执行视图函数

玩法1(常规)

from  django.utils.deprecation import MiddlewareMixin


class M1(MiddlewareMixin):
    def process_request(self, request):
        print('M1.request') 

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M1.process_view")
     
    def process_response(self, request, response):
        print('M1.response')
        return response 



class M2(MiddlewareMixin):
    def process_request(self, request):
        print('M2.request') 

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M2.process_view")
  
    def process_response(self, request, response):
        print('M2.response')
        return response

执行结果:

玩法2

既然 process_view 拿到视图函数的名称、参数,(不执行) 再执行process_view()方法,最后才去执行视图函数!

那可以在 执行process_view环节直接 把函数执行返回吗?

from  django.utils.deprecation import MiddlewareMixin


class M1(MiddlewareMixin):
    def process_request(self, request):
        print('M1.request')
                 # callback视图函数名称 callback_args,callback_kwargs 视图函数执行所需的参数
    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M1.process_view")
        response=callback(request,*callback_args,**callback_kwargs)
        return response
    def process_response(self, request, response):
        print('M1.response')
        return response



class M2(MiddlewareMixin):
    def process_request(self, request):
        print('M2.request')  

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M2.process_view")
    def process_response(self, request, response):
        print('M2.response')
        return response

执行结果:

结论:

如果process_view函数有返回值,跳转到最后一个中间件, 执行最后一个中间件的response方法,逐步返回。

和 process_request方法不一样哦!  request方法在当前中间件的response方法返回。

2、process_exception(self, request, exception)方法

from  django.utils.deprecation import MiddlewareMixin


class M1(MiddlewareMixin):
    def process_request(self, request):
        print('M1.request')
        
    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M1.process_view")

    def process_response(self, request, response):
        print('M1.response')
        return response

    def process_exception(self, request,exception):
        print('M1的process_exception')


class M2(MiddlewareMixin):
    def process_request(self, request):
        print('M2.request') 

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M2.process_view")

    def process_response(self, request, response):
        print('M2.response')
        return response

    def process_exception(self, request, exception):
        print('M2的process_exception')

我去 加了process_exception方法 咋啥也没执行呢?!!原来是process_exception默认不执行!!!

原来process_exception方法在 视图函数执行出错的时候才会执行

M1.request
M2.request
M1.process_view
M2.process_view
执行index
M2的process_exception
M1的process_exception
Internal Server Error: /index/
Traceback (most recent call last):
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\handlers\exception.py", line 41, in inner
    response = get_response(request)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\handlers\base.py", line 187, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "C:\Users\Administrator\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\handlers\base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "F:\untitled1\app01\views.py", line 7, in index
    int("ok")
ValueError: invalid literal for int() with base 10: 'ok'
M2.response
M1.response
[03/Jul/2017 16:43:59] "GET /index/ HTTP/1.1" 500 62663

1、执行完所有 request 方法 

2、执行 所有 process_view方法

3、如果视图函数出错,执行process_exception(最终response,process_exception的return值)

 如果process_exception 方法有了 返回值 就不再执行 其他中间件的 process_exception,直接执行response方法响应 

4.执行所有response方法

5.最后返回process_exception的返回值

M1.request
M2.request
M1.process_view
M2.process_view
执行index
M2的process_exception (有了return值,直接执行response)
M2.response
M1.response

process_exception的应用

在视图函数执行出错时,返回错误信息。这样页面就不会 报错了!

class M1(MiddlewareMixin):
    def process_request(self, request):
        print('M1.request')

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M1.process_view")

    def process_response(self, request, response):
        print('M1.response')
        return response

    def process_exception(self, request,exception):
        print('M1的process_exception')


class M2(MiddlewareMixin):
    def process_request(self, request):
        print('M2.request')

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M2.process_view")

    def process_response(self, request, response):
        print('M2.response')
        return response

    def process_exception(self, request, exception):
        print('M2的process_exception')
        return HttpResponse('出错了兄弟!!!')

3、process_template_response()

from  django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse

class M1(MiddlewareMixin):
    def process_request(self, request):
        print('M1.request')

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M1.process_view")

    def process_response(self, request, response):
        print('M1.response')
        return response


    def process_exception(self, request,exception):
        print('M1的process_exception')


class M2(MiddlewareMixin):
    def process_request(self, request):
        print('M2.request')

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M2.process_view")

    def process_response(self, request, response):
        print('M2.response')
        return response

    def process_exception(self, request, exception):
        print('M2的process_exception')

    def process_template_response(self,request,response):
        print('M2process_template_response')
        return response

process_template_response()默认不执行

 rocess_template_response()特性

 只有在视图函数的返回对象中有render方法才会执行!

并把对象的render方法的返回值返回给用户(注意不返回视图函数的return的结果了,而是返回视图函数 return值(对象)的render方法)

from  django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse


class M1(MiddlewareMixin):
    def process_request(self, request):
        print('M1.request')

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M1.process_view")

    def process_response(self, request, response):
        print('M1.response')
        return response


    def process_exception(self, request,exception):
        print('M1的process_exception')


class M2(MiddlewareMixin):
    def process_request(self, request):
        print('M2.request')

    def process_view(self, request,callback,callback_args,callback_kwargs ):
        print("M2.process_view")

    def process_response(self, request, response):
        print('M2.response')
        return response

    def process_exception(self, request, exception):
        print('M2的process_exception')

    def process_template_response(self,request,response):  #如果视图函数中的返回值 中有render方法,才会执行 process_template_response
        print('M2process_template_response')
        return response

视图函数

from django.shortcuts import render,HttpResponse

# Create your views here.
class Foo():
    def __init__(self,requ):
        self.req=requ
    def render(self):
        return HttpResponse('OKKKK')

def index(request):
    print("执行index")
    obj=Foo(request)
    return obj

执行结果

 应用:

既然process_template_respnse,不返回视图函数的return的结果,而是返回视图函数 return值(对象)的render方法;(多加了一个环节)

 就可以在 这个视图函数返回对象的 render方法里,做返回值的二次加工了!多加工几个,视图函数就可以随便使用了!

(好比 喷雾器有了多个喷头,换不同的喷头喷出不同水,返回值就可以也组件化了)

from django.shortcuts import render,HttpResponse

# Create your views here.
class Dict():   #对视图函数返回值做二次封装 !!
    def __init__(self,requ,msg):
        self.req=requ   
        self.msg=msg
    def render(self):
        a=self.msg #在render方法里面 把视图函数的 返回值 制作成字典 、列表等。。。 
                   #  如果新增了其他 一个视图函数直接,return对象 即可!不用每个视图函数都写 制作字典 列表 拼接的逻辑了
        return HttpResponse(a)    #

def index(request):
    print("执行index")
    obj=Dict(request,"vv")
    return obj

中间件应用场景

由于中间件工作在 视图函数执行前、执行后(像不像所有视图函数的装饰器!)适合所有的请求/一部分请求做批量处理

1、做IP限制

放在 中间件类的列表中,阻止某些IP访问了;

2、URL访问过滤

如果用户访问的是login视图(放过)

如果访问其他视图(需要检测是不是有session已经有了放行,没有返回login),这样就省得在 多个视图函数上写装饰器了!

3、缓存(还记得CDN吗?)

客户端请求来了,中间件去缓存看看有没有数据,有直接返回给用户,没有再去逻辑层 执行视图函数

csrf:  

  • 每次初始化一个项目时都能看到 django.middleware.csrf.CsrfViewMiddleware 这个中间件
  • 每次在模板里写 form 时都知道要加一个 {% csrf_token %} tag
  • 每次发 ajax POST 请求,都需要加一个 X_CSRFTOKEN 的 header

什么是 CSRF 

CSRF, Cross Site Request Forgery, 跨站点伪造请求。举例来讲,某个恶意的网站上有一个指向你的网站的链接,如果

某个用户已经登录到你的网站上了,那么当这个用户点击这个恶意网站上的那个链接时,就会向你的网站发来一个请求,

你的网站会以为这个请求是用户自己发来的,其实呢,这个请求是那个恶意网站伪造的。

 

Django 提供的 CSRF 防护机制

django 第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个 token 放在 cookie 里。然后每次 POST 请求都会带上这个 token,

这样就能避免被 CSRF 攻击。

  1. 在返回的 HTTP 响应的 cookie 里,django 会为你添加一个 csrftoken 字段,其值为一个自动生成的 token
  2. 在所有的 POST 表单时,必须包含一个 csrfmiddlewaretoken 字段 (只需要在模板里加一个 tag, django 就会自动帮你生成,见下面)
  3. 在处理 POST 请求之前,django 会验证这个请求的 cookie 里的 csrftoken 字段的值和提交的表单里的 csrfmiddlewaretoken 字段的值是否一样。如果一样,则表明这是一个合法的请求,否则,这个请求可能是来自于别人的 csrf 攻击,返回 403 Forbidden.
  4. 在所有 ajax POST 请求里,添加一个 X-CSRFTOKEN header,其值为 cookie 里的 csrftoken 的值

Django 里如何使用 CSRF 防护

  • 首先,最基本的原则是:GET 请求不要用有副作用。也就是说任何处理 GET 请求的代码对资源的访问都一定要是“只读“的。
  • 要启用 django.middleware.csrf.CsrfViewMiddleware 这个中间件
  • 再次,在所有的 POST 表单元素时,需要加上一个 {% csrf_token %} tag
  • 在渲染模块时,使用 RequestContext。RequestContext 会处理 csrf_token 这个 tag,  从而自动为表单添加一个名为 csrfmiddlewaretoken 的 input

缓存

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:

  • 数据库(默认)
  • 缓存
  • 文件
  • 缓存+数据库
  • 加密cookie

信号

Django中提供了“信号调度”,用于在框架执行操作时解耦。通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。

Model signals
    pre_init                    # django的modal执行其构造方法前,自动触发
    post_init                   # django的modal执行其构造方法后,自动触发
    pre_save                    # django的modal对象保存前,自动触发
    post_save                   # django的modal对象保存后,自动触发
    pre_delete                  # django的modal对象删除前,自动触发
    post_delete                 # django的modal对象删除后,自动触发
    m2m_changed                 # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
    class_prepared              # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
Management signals
    pre_migrate                 # 执行migrate命令前,自动触发
    post_migrate                # 执行migrate命令后,自动触发
Request/response signals
    request_started             # 请求到来前,自动触发
    request_finished            # 请求结束后,自动触发
    got_request_exception       # 请求异常后,自动触发
Test signals
    setting_changed             # 使用test测试修改配置文件时,自动触发
    template_rendered           # 使用test测试渲染模板时,自动触发
Database Wrappers
    connection_created          # 创建数据库连接时,自动触发

对于Django内置的信号,仅需注册指定信号,当程序执行相应操作时,自动触发注册函数:

from django.core.signals import request_finished
    from django.core.signals import request_started
    from django.core.signals import got_request_exception

    from django.db.models.signals import class_prepared
    from django.db.models.signals import pre_init, post_init
    from django.db.models.signals import pre_save, post_save
    from django.db.models.signals import pre_delete, post_delete
    from django.db.models.signals import m2m_changed
    from django.db.models.signals import pre_migrate, post_migrate

    from django.test.signals import setting_changed
    from django.test.signals import template_rendered

    from django.db.backends.signals import connection_created


    def callback(sender, **kwargs):
        print("xxoo_callback")
        print(sender,kwargs)

    xxoo.connect(callback)
    # xxoo指上述导入的内容

 

from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")

自定义信号

a.定义信号

import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

b.注册信号

def callback(sender, **kwargs):
    print("callback")
    print(sender,kwargs)
  
pizza_done.connect(callback) 

c.触发信号

from 路径 import pizza_done
  
pizza_done.send(sender='seven',toppings=123, size=456)

 

由于内置信号的触发者已经集成到Django中,所以其会自动调用,而对于自定义信号则需要开发者在任意位置触发

 

分页

 一、Django内置分页

二、自定义分页

分页功能在每个网站都是必要的,对于分页来说,其实就是根据用户的输入计算出应该在数据库表中的起始位置。

1、设定每页显示数据条数

2、用户输入页码(第一页、第二页...)

3、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置

4、在数据表中根据起始位置取值,页面上输出数据 

需求又来了,需要在页面上显示分页的页面。如:[上一页][1][2][3][4][5][下一页] 

1、设定每页显示数据条数 

2、用户输入页码(第一页、第二页...)

3、设定显示多少页号

4、获取当前数据总条数

5、根据设定显示多少页号和数据总条数计算出,总页数

6、根据设定的每页显示条数和当前页码,计算出需要取数据表的起始位置 

7、在数据表中根据起始位置取值,页面上输出数据

8、输出分页html,如:[上一页][1][2][3][4][5][下一页]

总结,分页时需要做三件事:

  • 创建处理分页数据的类
  • 根据分页数据获取数据
  • 输出分页HTML,即:[上一页][1][2][3][4][5][下一页]
#!/usr/bin/env python
# _*_coding:utf-8_*_
from django.utils.safestring import mark_safe
 
class PageInfo(object):
    def __init__(self,current,totalItem,peritems=5):
        self.__current=current
        self.__peritems=peritems
        self.__totalItem=totalItem
    def From(self):
        return (self.__current-1)*self.__peritems
    def To(self):
        return self.__current*self.__peritems
    def TotalPage(self):  #总页数
        result=divmod(self.__totalItem,self.__peritems)
        if result[1]==0:
            return result[0]
        else:
            return result[0]+1
 
def Custompager(baseurl,currentPage,totalpage):  #基础页,当前页,总页数
    perPager=11
    #总页数<11
    #0 -- totalpage
    #总页数>11
        #当前页大于5 currentPage-5 -- currentPage+5
            #currentPage+5是否超过总页数,超过总页数,end就是总页数
        #当前页小于5 0 -- 11
    begin=0
    end=0
    if totalpage <= 11:
        begin=0
        end=totalpage
    else:
        if currentPage>5:
            begin=currentPage-5
            end=currentPage+5
            if end > totalpage:
                end=totalpage
        else:
            begin=0
            end=11
    pager_list=[]
    if currentPage<=1:
        first="<a href=''>首页</a>"
    else:
        first="<a href='%s%d'>首页</a>" % (baseurl,1)
    pager_list.append(first)
 
    if currentPage<=1:
        prev="<a href=''>上一页</a>"
    else:
        prev="<a href='%s%d'>上一页</a>" % (baseurl,currentPage-1)
    pager_list.append(prev)
 
    for i in range(begin+1,end+1):
        if i == currentPage:
            temp="<a href='%s%d' class='selected'>%d</a>" % (baseurl,i,i)
        else:
            temp="<a href='%s%d'>%d</a>" % (baseurl,i,i)
        pager_list.append(temp)
    if currentPage>=totalpage:
        next="<a href='#'>下一页</a>"
    else:
        next="<a href='%s%d'>下一页</a>" % (baseurl,currentPage+1)
    pager_list.append(next)
    if currentPage>=totalpage:
        last="<a href=''>末页</a>"
    else:
        last="<a href='%s%d'>末页</a>" % (baseurl,totalpage)
    pager_list.append(last)
    result=''.join(pager_list)
    return mark_safe(result)   #把字符串转成html语言

 

posted on 2018-01-05 10:37  龚旭1994  阅读(245)  评论(0编辑  收藏  举报