23Django-文章列表缓存

缓存分为三种,

第一种是直接使用装饰器cache_page,优点是方便,缺点是无法自行删除缓存,只能等待过期时间让系统自己删除,

示例:

#直接再文章列表方法上打上缓存装饰器,过期时间时30秒
@method_decorator(cache_page(30))
def get(self,request,username):
    pass

第二种使用局部缓存cache.set/get,优点灵活,存储成本最优,删除成本呢低,缺点,代码成本实现成本高

伪代码示例:

#在模型类中定义classmethod方法
class Topic
    @classmethod
    def get_topic_list(cls):
        if cache:
            return cache
        data = cls.objects.filter()
        return data

第三种是我们自定义装饰器,优点是我们根据我们当前的业务情况,按照自己的要求生成cache,缺点是需要自己写方法写逻辑:

我们选择方案3:

1在tools下面创建cache_dec.py

自定义的普通装饰器是2层结构,而要实现传参,我们需要在普通装饰器的外层再定义一层:

#能传参的装饰器骨架
def cache_set(expire):
    def _cache_set(func): #第二层函数
        def wrapper(request,*args,**kwargs): #第一层函数
            #区分场景-只做列表页
            #生成正确的cache key 【访客访问和博主访问】
            #判断当前cache key是否有缓存,有就执行,没有就往下继续
            #执行视图
            #存储缓存【推荐使用cache对象/set/get执行存储】
            #返回响应
            return func(request,*args,**kwargs)
        return wrapper #返回第一层函数
    return _cache_set #返回第二层函数

 2在装饰器骨架的最里层完成逻辑:

from django.core.cache import cache

from .logging_dec import get_user_by_request

#自定义缓存装饰器
def cache_set(expire):
    def _cache_set(func):
        def wrapper(request,*args,**kwargs):
            #如果是详情页就直接返回func,因为我们本次只做列表页
            if 't_id' in request.GET:
                return func(request,*args,**kwargs)
            #确认访客身份
            #通过请求拿到访问者
            visitor_user = get_user_by_request(request)
            visitor_username = None
            if visitor_user:
                visitor_username = visitor_user.username
            #拿到作者的用户名
            author_username = kwargs['username']
            print('visitor is %s'%(visitor_username))
            print('author is %s'%(author_username))
            #获取当前访问的路由
            full_path = request.get_full_path()
            #给博主定义cache key
            if visitor_username == author_username:
                cache_key = 'topics_cache_self_%s'%(full_path)
            #给访客定义cache key
            else:
                cache_key = 'topics_cache_%s'%(full_path)
            print('当前cacke_key是:',cache_key)
            #----判断是否有缓存,有缓存就直接返回,没缓存再执行视图
            #尝试拿一下有没有缓存,如果有就直接return缓存
            res = cache.get(cache_key)
            if res:
                print('有缓存,开始走缓存')
                return res
            #执行视图
            #拿到视图返回结果并赋值给变量res
            res = func(request,*args,**kwargs)
            #存储缓存,过期时间以传过来的expire为准
            cache.set(cache_key,res,expire)
            #返回响应
            return res
        return wrapper
    return _cache_set

2回到发布文章的post视图中定义一个删除缓存的方法:

#定义一个删缓存的方法
    #在发表文章和更新文章时调用这个方法就可以删除缓存
    def clear_topics_cache(self,request):
        #把不带查询字符串的路由查出来
        path = request.path_info
        #cache key前缀
        cache_key_p = ['topics_cache_self_', 'topics_cache_']
        #cache后缀有2种情况,1时空不带查询字符串,2是带分类字符串的
        cache_key_h = ['','?category=python','?category=linux']
        #准备一个存放cache_key的容器
        all_keys = []
        #开始拼接cache key,并把所有的key存入到空列表内
        for key_p in cache_key_p:
            for key_h in cache_key_h:
                all_keys.append(key_p + path + key_h)
        #批量删除所有的cache key对应的缓存数据
        print('clear caches is', all_keys)
        cache.delete_many(all_keys)

3在文章发布后执行删除缓存操作:

#创建文章发布数据
        Topic.objects.create(title=title,content=content,limit=limit,category=category,introduce=introduce,author=username)
        #发表文章之后删除缓存
        self.clear_topics_cache(request)

4完整代码欣赏:

import json

from django.core.cache import cache
from django.http import JsonResponse
from django.shortcuts import render
from django.views import View
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page

from tools.logging_dec import logging_check,get_user_by_request
from .models import Topic
from user.models import UserProfile
from tools.cache_dec import cache_set
# Create your views here.
#发表文章
from django.forms.models import model_to_dict
class TopicViews(View):
    #定义一个删缓存的方法
    #在发表文章和更新文章时调用这个方法就可以删除缓存
    def clear_topics_cache(self,request):
        #把不带查询字符串的路由查出来
        path = request.path_info
        #cache key前缀
        cache_key_p = ['topics_cache_self_', 'topics_cache_']
        #cache后缀有2种情况,1时空不带查询字符串,2是带分类字符串的
        cache_key_h = ['','?category=python','?category=linux']
        #准备一个存放cache_key的容器
        all_keys = []
        #开始拼接cache key,并把所有的key存入到空列表内
        for key_p in cache_key_p:
            for key_h in cache_key_h:
                all_keys.append(key_p + path + key_h)
        #批量删除所有的cache key对应的缓存数据
        print('clear caches is', all_keys)
        cache.delete_many(all_keys)

    #组装文章详情页数据
    def make_topic_res(self,author,author_topic,is_self):
        #博主访问自己
        if is_self:
            #下一篇
            next_topic = Topic.objects.filter(id__gt=author_topic.id,author=author).first()
            #上一篇
            last_topic = Topic.objects.filter(id__lt=author_topic.id,author=author).last()
        else:
            next_topic = Topic.objects.filter(id__gt=author_topic.id,author=author,limit='public').first()
            last_topic = Topic.objects.filter(id__lt=author_topic.id,author=author,limit='public').last()
            print('next_topic的值是:',next_topic)

        #下一篇文章的id和title
        next_id = next_topic.id if next_topic else None
        next_title = next_topic.title if next_topic else ''
        #上一篇文章的id和title
        last_id = last_topic.id if last_topic else None
        last_title = last_topic.title if last_topic else ''

        res = {'code':200,'data':{}}
        res['data']['nickname'] = author.nickname
        res['data']['title'] = author_topic.title
        res['data']['category'] = author_topic.category
        res['data']['created_time'] = author_topic.create_time.strftime('%Y-%m-%d %H:%M:%S')
        res['data']['content'] = author_topic.content
        res['data']['introduce'] = author_topic.introduce
        res['data']['author'] = author.username
        res['data']['last_id'] = last_id
        res['data']['last_title'] = last_title
        res['data']['next_id'] = next_id
        res['data']['next_title'] = next_title
        res['data']['messages'] = []
        res['data']['messages_count'] = 0
        return res


    #定义一个方法返回文档里要求的字典
#{‘code’:200, ‘data’:{‘nickname’:’linuxTang’, ‘topics’:[{‘id’:1, ‘title’:’A’, ‘category’:’python’, ‘created_time’:’2021-12-15 21:07:20’, ‘introduce’:’AAA’, ‘author’:’qq66907360’}]}}

    def make_topics_res(self,author,author_topics):
        res = {'code':200,'data':{}}
        topics_res = []
        #遍历出每一篇文章
        for topic in author_topics:
            #组织数据
            #{‘id’:1, ‘title’:’A’, ‘category’:’python’, ‘created_time’:’2021-12-15 21:07:20’, ‘introduce’:’AAA’, ‘author’:’qq66907360’}
            d={}
            d['id'] = topic.id
            d['title'] = topic.title
            d['category'] = topic.category
            d['created_time'] = topic.create_time.strftime("%Y-%m-%d %H:%M:%S")
            d['introduce'] = topic.introduce
            d['author'] = topic.author.username
            #把组织好的字典添加到列表中
            #[{‘id’:1, ‘title’:’A’, ‘category’:’python’, ‘created_time’:’2021-12-15 21:07:20’, ‘introduce’:’AAA’, ‘author’:’qq66907360’}]
            topics_res.append(d)
        #‘topics’:[{‘id’:1, ‘title’:’A’, ‘category’:’python’, ‘created_time’:’2021-12-15 21:07:20’, ‘introduce’:’AAA’, ‘author’:’qq66907360’}]
        res['data']['topics'] = topics_res
        ##{‘code’:200, ‘data’:{‘nickname’:’linuxTang’
        res['data']['nickname'] = author.nickname
        print(res)
        return res

    @method_decorator(logging_check) #把自定义的方法装饰器转换成类方法可以使用的类装饰器
    #文章发布
    def post(self,request,username):
        # 拿到当前登录的用户
        username = request.myuser
        #取出前端传过来的数据
        json_str = request.body
        #把json串转换成字典
        json_obj = json.loads(json_str)
        #从字典里取数据
        title = json_obj.get('title')
        content = json_obj.get('content')
        content_text = json_obj.get('content_text')
        introduce = content_text[:30] #截取简介
        limit = json_obj.get('limit')
        category = json_obj.get('category')
        #对文章权限进行判断,防止外部垃圾
        if limit not in ['public','private']:
            result = {'code':10300,'error':'文章权限关键字非法'}
            return JsonResponse(result)
        #对文章分类做判断,也是为了安全
        if category not in ['python','linux']:
            result = {'code': 10301, 'error': '文章分类关键字非法'}
            return JsonResponse(result)
        #创建文章发布数据
        Topic.objects.create(title=title,content=content,limit=limit,category=category,introduce=introduce,author=username)
        #发表文章之后删除缓存
        self.clear_topics_cache(request)

        return JsonResponse({'code':200})


    #查看文章
    @method_decorator(cache_set(30))
    def get(self,request,username):
        print('----view==in----')
        #访问博客的人分类两类,一类是访客,另外一类是博主本人,先判断传过来的用户名在数据库中是否存在
        #确认博主身份
        try:
            author = UserProfile.objects.get(username=username)
            #dict_author = model_to_dict(author)
            #dict_author['avatar'] = author.avatar.url
        except Exception as e:
            result = {'code':301,'error':'作者不存在'}
            return JsonResponse(result)
        #判断访客身份
        visitor = get_user_by_request(request)

        visitor_username = None
        if visitor:
            visitor_username = visitor.username
        print('--以上代码基本都是确定访客身份的--')

        #根据获取前端的字符串拆分业务
        t_id = request.GET.get('t_id')
        print(t_id)
        if t_id:
            #获取指定的文章数据
            t_id = int(t_id)
            #如果is_self=True就是博主自己,反之就是访客,默认设置为访客
            is_self = False
            if visitor_username == username:
                is_self = True
            #如果是博主,就从数据库里查出博主的t_id,否则你只能拿到博主公开的t_id
                try:
                    author_topic = Topic.objects.get(id=t_id,author_id=username)
                except Exception as e:
                    result = {'code':20211217,'error':'博主身份异常'}
                    return JsonResponse(result)
            else:
                try:
                    author_topic = Topic.objects.get(id=t_id,author_id=username,limit='public')
                except Exception as e:
                    result = {'code':302,'error':'当前文章不存在或者你查询的字符串属于作者隐私'}
                    return JsonResponse(result)
            #响应前端数据
            res = self.make_topic_res(author,author_topic,is_self)
            #print(res)
            return JsonResponse(res)
        else:
            print('--以下代码基本就是获取具体查询字符串条件--')

            #温柔取值,没有返回None
            category = request.GET.get('category')
            #判断是否查分类了
            if category in ['python','linux']:

                #如果访客的用户名等于当前博主的用户名意味着博主再访问自己的博客
                if visitor_username == username:
                    author_topics = Topic.objects.filter(author_id=username,category=category)
                else:
                    #否则的话只能看公开的
                    author_topics = Topic.objects.filter(author_id=username,limit='public',category=category)
            else:
                if visitor_username == username:
                    author_topics = Topic.objects.filter(author_id=username)
                else:
                    #否则的话只能看公开的
                    author_topics = Topic.objects.filter(author_id=username,limit='public')
            #把博主的和查到的文章作为参数都传到响应方法make_topics_res里
            #return JsonResponse({'author':dict_author,'list_arctile':[model_to_dict(i) for i in author_topics]})
            res = self.make_topics_res(author, author_topics)
            print(res)
            return JsonResponse(res)
#我知道原因,但不知如何解决好,我刚找了一个大神,给我弄了一下,但是请求不到数据,因为前端页面都是渲染的,所以波必须按照前端的要求写逻辑,我演示给你看一下

 

上一篇:【测试开发】知识点-项目中使用 Spring Cache + Redis 缓存数据


下一篇:mysql的语句解析详解