23Django-文章列表快取
阿新 • • 發佈:2021-12-17
快取分為三種,
第一種是直接使用裝飾器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): #第二層函式 defwrapper(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) #我知道原因,但不知如何解決好,我剛找了一個大神,給我弄了一下,但是請求不到資料,因為前端頁面都是渲染的,所以波必須按照前端的要求寫邏輯,我演示給你看一下