不忘初心,
牢记使命。

Django中缓存的用法---自定义缓存装饰器

2021-05-19 大聪明 0评论 190 0喜欢

缓存

缓存--把高频读取的数据,防止到更快的存储介质里

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

场景:博客列表页,电商商品详情页,特点就是数据变动频率较少。

Django中缓存的用法

缓存方案

把数据库中大量的数据查询的结果,放在另一个库中,下次就可以很快从另一个库中拿到数据。

# 需要手动执行数据库命令,创建缓存表
# python manage.py createcachetable
# mysql
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的缓存数据   
        }
    }
}

数据缓存到服务器内存中

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.locmem.LocMemCache",
        "LOCATION": "unique-snowflake",  # 随便起
    }
}

存储到文件中

CACHES = {
    "default": {
        "BACKEND": "django.core.cache.backends.filebased.FileBasedCache",
        "LOCATION": "/var/tmp/django_cache",  # 文件路径
    }
}

使用

  1. 视图函数中整体缓存策略

    from django.views.decorators.cache import cache_page
    
    # 整体缓存策略
    @cache_page(30)   # 秒
    def my_view(request):
        pass
    
  2. 路由中整体缓存策略

    from django.views.decorators.cache import cache_page
    
    urlpatterns = [
        # 整体缓存策略
        path('foo/', cache_page(60)(my_view))
    ]
    
  3. 局部缓存,灵活性高

    1. 使用caches['CACHE配置key']导入具体对象

      from django.core.cache import caches
      
      cache1 = caches['myalias']  # default默认
      cache2 = caches['myalias_2']
      
    2. 相当于直接引入CACHES配置项中的default项

      from django.core.cache import cache
      

缓存api的使用

from django.core.cache import cache

cache.set(key, value, timeout)   # 返回值None

cache.get(ket)  # 返回值:为key的具体指,如果无返回None

cache.add(key, value)   # 只在key不存在时生效  返回:True成功  False失败

cache.get_or_set(key, value, timeout)

cache.set_many(dict, timeout)   # dict:key和value的字典

cache.get_many(key_list)

cache.delete(key)

cache.delete_many(key_list)

浏览器缓存策略

优先访问自己的缓存,用户的电脑上的一个存储空间。

强缓存

  1. 响应头-Expires

    存到什么时候,绝对时间

  2. 响应头-Cache-Control

    存多长时间,相对时间

协商缓存

针对一些静态文件,大图片强缓存过期后,浏览器先跟服务器商量一下,你的静态文件和音视频有没有变啊,没变的话我接着存,变的话我在更新一下即可

  1. Last-Modeified响应头和If-Modified-Since请求头 304触发的协商缓存
  2. Etag响应头和If-None-Match

博客文章缓存方案

  1. cache_page(过期时间s)装饰器

    优点:简单

    缺点:权限问题,如果超级管理员看来一下文章,所有文章全缓存了,访客到访时可能会在缓存中看到博主的私有文章。

    例如我的博客,管理员用户登陆后在侧边栏是显示的管理员用户的信息,包括用户名、头像等,放入缓存。此时如果有另一个访客登陆后,显示的本该是他自己的信息,但是确实管理员的。因为直接在缓存中拿数据,此时存的是管理员的信息。

# 例如
from django.utils.decorators import method_decorator
class Index(View):
    @method_decorator(cache_page(60))
    def get(self, request, en_us_c=None, en_us_tag=None):
        pass

删除缓存成本过高(出现新旧数据不一致)

  1. 局部缓存--cache.set/get

    优点:灵活、存储成本最优、删除成本低

    缺点:代码实现成本较高

    案例:

    ​ 在模型类中,定义classmethod

    class Topic:
        @classmethod
        def get_topic_list(cls,):
            if cache:
                return cache
            data = cls.objects.filter()
            # cache in
            return data
    
  2. 自己写缓存装饰器

    创建文章前后把所有的cache清空

    # 可传参的装饰器写法
    def cache_set(expire):
    
        def _cache_set(func):
            def wrapper(request, *args, **kwargs):
                # 区分场景  -  只做列表页
                # 生成 正确的cache_key  [访客访问  和   博主访问]
                # -----判断是否有缓存,有缓存直接返回
                # 执行视图
                # 存储缓存    直接用cache对象/set/get
                # 返回响应
                return func(request, *args, **kwargs)
            return wrapper
    
        return _cache_set
    

    我的博客缓存装饰器的实现

    from blog import models
    from django.core.cache import cache
    
    # 可传参的装饰器写法
    def cache_set(expire):
    
        def _cache_set(func):
            def wrapper(request, *args, **kwargs):
                # 文章列表
                login_user_id = request.session.get('user_id')
                cache_key = 'admin_cache'
                if login_user_id:
                    login_user = models.UserInfo.objects.get(id=login_user_id)
                    if not login_user.is_admin:
                        cache_key = 'user_cache'
                # -----判断是否有缓存,有缓存直接返回
                res = cache.get(cache_key)
                if res:
                    return res
                # 执行视图
                res = func(request, *args, **kwargs)
                # 存储缓存    直接用cache对象/set/get
                cache.set(cache_key, res, expire)
                # 返回响应
                return res
            return wrapper
    
        return _cache_set
    

    使用

    # 使用
    from blog.utils.cache_dec import cache_set
    from django.utils.decorators import method_decorator
    class Index(View):
        @method_decorator(cache_set(60))
        def get(self, request, en_us_c=None, en_us_tag=None):
            pass
    

发文章时要清空缓存达到立见性

如果不清缓存的话,有人访问了一下主页,然后你在缓存的失效时间内(如果设置1分钟过期缓存,那就是一分钟内)发了一篇文章,然后那个人此时是看不到你的文章的,因为是在缓存中拿的数据。所以没有实时性。当然你如果可以容忍这个时间的话也没问题。不容忍,就在发文章时清空缓存

我在admin.py中通过重写ModelAdmin来实现的,之前写过我懒得写文章的简要desc,就取文章前100个字符作为desc,所以现在我们正好在那里进行一个清空缓存操作就行了。在这里清空,发文章或者更新文章都会走这里,一举两得。

class ArticleForm(forms.ModelForm):
    class Meta:
        model = models.Article
        fields = "__all__"

    def clean_desc(self):

        desc = self.cleaned_data['desc']
        content = self.data['content']
        renderer = mistune.Renderer(escape=True, hard_wrap=True)
        # renderer = HighlightRenderer()
        mk = mistune.Markdown(renderer=renderer)
        output = mk(content)
        soup = BeautifulSoup(output, 'html.parser')
        content = soup.text[:150]
        print(desc)

        #  发文章时删除对应的缓存key
        if cache.get(settings.CUSTOM_CACHE_ADMIN):
            cache.delete(settings.CUSTOM_CACHE_ADMIN)
        if cache.get(settings.CUSTOM_CACHE_USER):
            cache.delete(settings.CUSTOM_CACHE_USER)

        if desc:
            return desc
        else:
            desc = content
            return desc


@admin.register(models.Article)
class ArticleAdmin(admin.ModelAdmin):
    form = ArticleForm

发表评论 取消回复

电子邮件地址不会被公开。

请输入正确格式的qq邮箱
请输入以http或https开头的URL,格式如:https://libo_sober.top