Django-缓存 & 中间件(六)

缓存

什么是缓存?

缓存是一类可以更快的读取数据的介质统称,也指其它可以加快数据读取的存储方式。一般用来存储临时数据,常用介质的是读取速度很快的内存

为什么使用缓存?

视图渲染有一定成本,对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数

案例分析

from django.shortcuts import render

def index(request):
    # 时间复杂度极高的渲染
    book_list = Book.objects.all()  #-> 此处假设耗时2s
    return render(request, 'index.html', locals())

优化思想

given a URL, try finding that page in the cache

if the page is in the cache:
    return the cached page
else:
    generate the page
    save the generated page in the cache (for next time)
    return the generated page

使用缓存场景:

1,博客列表页

2,电商商品详情页

3,缓存导航及页脚

Django中设置缓存

Django中提供多种缓存方式,如需使用需要在settings.py中进行配置

1,数据库缓存 mysite7 改配置 migrate , 添加缓存配置项 createcachetable

Django可以将其缓存的数据存储在您的数据库中

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
        'LOCATION': 'my_cache_table',
        'TIMEOUT': 300,  #缓存保存时间 单位秒,默认值为300, 
        'OPTIONS':{
            'MAX_ENTRIES': 300, #缓存最大数据条数
            'CULL_FREQUENCY': 2,#缓存条数达到最大值时 删除1/x的缓存数据
        }
    }
}

创建缓存表

python3 manage.py createcachetable

2,文件系统缓存

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION': '/var/tmp/django_cache',#这个是文件夹的路径
        #'LOCATION': 'c:\test\cache',#windows下示例
    }
}

3, 本地内存缓存

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION': 'unique-snowflake'
    }
}

Django中使用缓存

  • 在视图View中使用
  • 在路由URL中使用
  • 在模板中使用

在视图View中使用cache

from django.views.decorators.cache import cache_page

@cache_page(30)  -> 单位s
def my_view(request):
    ...

在路由中使用

from django.views.decorators.cache import cache_page

urlpatterns = [
    path('foo/', cache_page(60)(my_view)  ),
]

在模板中使用

{% load cache %}        

{% cache 500 sidebar username %}
    .. sidebar for logged in user ..
{% endcache %}
  • 缓存api

    作用:局部缓存部分结果

    使用:

    #指定配置引入
    from django.core.cache import caches
    cache1 = caches['myalias']
    cache2 = caches['myalias_2']
    
    #默认配置引入【指的配置中的default项】 等同于 caches['default']
    from django.core.cache import cache
    
    #常规命令 set
    #key: 字符串类型
    #value: Python对象
    #timeout:缓存存储时间  默认值为settings.py CACHES对应配置的TIMEOUT
    #返回值:None
    cache.set('my_key', 'myvalue', 30)
    
    #常规命令 get
    #返回值:为key的具体值,如果没有数据,则返回None
    cache.get('my_key')
    #可添加默认值,如果没取到返回默认值
    cache.get('my_key', 'default值')
    
    #常规命令 add 只有在key不存在的时候 才能设置成功
    #返回值 True or False
    cache.add('my_key', 'value') #如果my_key已经存在,则此次赋值失效
    
    #常规命令 get_or_set 如果未获取到数据 则执行set操作
    #返回值 key的值
    cache.get_or_set('my_key', 'value', 10)
    
    #常规命令 get_many(key_list) set_many(dict,timeout)
    #返回值  set_many:返回插入不成功的key数组 
    #       get_many:取到的key和value的字典
    >>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
    >>> cache.get_many(['a', 'b', 'c'])
    {'a': 1, 'b': 2, 'c': 3}
    
    #常规命令 delete
    #返回值  None
    cache.delete('my_key')
    
    #常规命令 delete_many
    #返回值  成功删除的数据条数
    cache.delete_many(['a', 'b', 'c'])
    
    

浏览器中的缓存

浏览器缓存分类:

强缓存

不会向服务器发送请求,直接从缓存中读取资源

1,Expires

缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点

Expires:Thu, 02 Apr 2030 05:14:08 GMT

Expires 是 HTTP/1 的产物,受限于本地时间,如 果修改了本地时间,可能会造成缓存失效

2, Cache-Control

在HTTP/1.1中,Cache-Control主要用于控制网页缓存。比如当Cache-Control:max-age=120代表请求创建时间后的120秒,缓存失效

协商缓存

协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程

1,Last-Modified和If-Modified-Since

第一次访问时,服务器会返回 

Last-Modified: Fri, 22 Jul 2016 01:47:00 GMT

浏览器下次请求时 携带If-Modified-Since这个header , 该值为 Last-Modified

服务器接收请求后,对比结果,若资源未发生改变,则返回304, 否则返回200并将新资源返回给浏览器

缺点:只能精确到秒,容易发生单秒内多次修改,检测不到

2,ETag和If-None-Match

Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成

流程同上

对比 Last-Modified VS ETag

1,精度不一样 - Etag 高

2,性能上 - Last-Modifi 高

3,优先级 - Etag 高

中间件 Middleware

  • 中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。

  • 每个中间件组件负责做一些特定的功能。例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话将用户与请求关联起来。

  • 中间件类:

    • 中间件类须继承自 django.utils.deprecation.MiddlewareMixin
    • 中间件类须实现下列五个方法中的一个或多个:
      • def process_request(self, request): 执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象
      • def process_view(self, request, callback, callback_args, callback_kwargs): 调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象
      • def process_response(self, request, response): 所有响应返回浏览器 被调用,在每个请求上调用,返回HttpResponse对象
      • def process_exception(self, request, exception): 当处理过程中抛出异常时调用,返回一个HttpResponse对象
      • def process_template_response(self, request, response): 在视图函数执行完毕且试图返回的对象中包含render方法时被调用;该方法需要返回实现了render方法的响应对象
    • 注: 中间件中的大多数方法在返回None时表示忽略当前操作进入下一项事件,当返回HttpResponese对象时表示此请求结束,直接返回给客户端
  • 编写中间件类:

# file : middleware/mymiddleware.py
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

class MyMiddleWare(MiddlewareMixin):
    def process_request(self, request):
        print("中间件方法 process_request 被调用")

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("中间件方法 process_view 被调用")

    def process_response(self, request, response):
        print("中间件方法 process_response 被调用")
        return response
  • 注册中间件:
# file : settings.py
MIDDLEWARE = [
    ...
       ]
  • 中间件的执行过程


  • 练习
    • 用中间件实现强制某个IP地址只能向/test 发送 5 次GET请求
    • 提示:
      • request.META['REMOTE_ADDR'] 可以得到远程客户端的IP地址
      • request.path_info 可以得到客户端访问的GET请求路由信息

跨站请求伪造攻击 CSRF

  • 跨站请求伪造攻击

    • 某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作,这就是跨站请求伪造(CSRF,即Cross-Site Request Forgey)。
  • 说明:

  • CSRF中间件和模板标签提供对跨站请求伪造简单易用的防护。

  • 作用:

    • 不让其它表单提交到此 Django 服务器
  • 防范步骤:

    1. settings.py中确认 MIDDLEWARE 中 django.middleware.csrf.CsrfViewMiddleware是否打开
    2. 模板中,form标签下添加如下标签
     {% csrf_token %}
    
  • 如果某个视图不需要django进行csrf保护,可以用装饰器关闭对此视图的检查

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def my_view(request):
    return HttpResponse('Hello world')

分页

  • 分页是指在web页面有大量数据需要显示,为了阅读方便在每个页页中只显示部分数据。
  • 好处:
    1. 方便阅读
    2. 减少数据提取量,减轻服务器压力。
  • Django提供了Paginator类可以方便的实现分页功能
  • Paginator类位于django.core.paginator 模块中。

Paginator对象

  • 负责分页数据整体的管理

  • 对象的构造方法

    • paginator = Paginator(object_list, per_page)
    • 参数
      • object_list 需要分类数据的对象列表
      • per_page 每页数据个数
    • 返回值:
      • Paginator的对象
  • Paginator属性

    • count:需要分类数据的对象总数
    • num_pages:分页后的页面总数
    • page_range:从1开始的range对象, 用于记录当前面码数
    • per_page 每页数据的个数
  • Paginator方法

    • page(number)
      • 参数 number为页码信息(从1开始)
      • 返回当前number页对应的页信息
      • 如果提供的页码不存在,抛出InvalidPage异常
  • Paginator异常exception

    • InvalidPage:总的异常基类,包含以下两个异常子类
      • PageNotAnInteger:当向page()传入一个不是整数的值时抛出
      • EmptyPage:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出

Page对象

  • 负责具体某一页的数据的管理

  • 创建对象
    Paginator对象的page()方法返回Page对象

    page = paginator.page(页码)
    
  • Page对象属性

    • object_list:当前页上所有数据对象的列表
    • number:当前页的序号,从1开始
    • paginator:当前page对象相关的Paginator对象
  • Page对象方法

    • has_next():如果有下一页返回True
    • has_previous():如果有上一页返回True
    • has_other_pages():如果有上一页或下一页返回True
    • next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常
    • previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvalidPage异常
    • len():返回当前页面对象的个数
  • 说明:

  • Page 对象是可迭代对象,可以用 for 语句来 访问当前页面中的每个对象

  • 参考文档 https://docs.djangoproject.com/en/2.2/topics/pagination/

  • 分页示例:
    • 视图函数
    from django.core.paginator import Paginator
    def book(request):  
        bks = Book.objects.all()
        paginator = Paginator(bks, 10)
        cur_page = request.GET.get('page', 1)  # 得到默认的当前页
        page = paginator.page(cur_page)
        return render(request, 'bookstore/book.html', locals())
    
  • 模板设计
    <html>
    <head>
        <title>分页显示</title>
    </head>
    <body>
    {% for b in page %}
        <div>{{ b.title }}</div>
    {% endfor %}
    
    {% if page.has_previous %}
    <a href="{% url 'book' %}?page={{ page.previous_page_number }}">上一页</a>
    {% else %}
    上一页
    

{% endif %}

{% for p in paginator.page_range %}
    {% if p == page.number %}
        {{ p }}
    {% else %}
        <a href="{% url 'book' %}?page={{ p }}">{{ p }}</a>
    {% endif %}

{% endfor %}

{% if page.has_next %}
<a href="{% url 'book' %}?page={{ page.next_page_number }}">下一页</a>
{% else %}
下一页
{% endif %}
</body>

</html>
```

文件下载

Django可直接在视图函数中生成csv文件 并响应给浏览器

import csv
from django.http import HttpResponse
from .models import Book

def make_csv_view(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="mybook.csv"'
    all_book = Book.objects.all()
    writer = csv.writer(response)
    writer.writerow(['id', 'title'])
    for b in all_book:    
        writer.writerow([b.id, b.title])

    return response
  • 响应获得一个特殊的MIME类型text / csv。这告诉浏览器该文档是CSV文件,而不是HTML文件
  • 响应会获得一个额外的Content-Disposition标头,其中包含CSV文件的名称。它将被浏览器用于“另存为...”对话框
  • 对于CSV文件中的每一行,调用writer.writerow,传递一个可迭代对象,如列表或元组。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容

  • 前言 如果使用缓存中间件(在settings里面加上设置)那么中间件将会基于URL对Django的页面进行缓存。启...
    LumiaXu阅读 1,044评论 0 0
  • 中间件 Middleware 中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系...
    南坡三舅阅读 867评论 0 0
  • 模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。软件设计中通常用耦合度和内聚...
    riverstation阅读 2,070评论 0 8
  • 中间件是 Django 用来处理请求和响应的钩子框架。它是一个轻量级的、底层级的“插件”系统,用于全局性地控制Dj...
    liujiangblog阅读 1,286评论 0 4
  • 一、Django框架前言知识: 1、C/S和B/S的区别: C/S结构软件:客户端/服务端软件,即客户端要自己下载...
    月下独酌123阅读 4,531评论 0 36