實現方法:
1、根據用戶的瀏覽器生成唯一的用戶id,並且把其放置在用戶的瀏覽器cookie中。
(1)我們可以使用Python內置的uuid庫來生成唯一的用戶id:
import uuid
uid = uuid.uuid4().hex
(2)在一個Web系統中,顯示是在請求的越早階段鑑定/標記用戶越好。所以我們在Django系統的middleware中去給用戶設置唯一的id,並且放置在用戶的瀏覽器cookie中。因此,我們在文章對應的APP目錄下新建middleware文件夾,並在middleware文件夾中新建__init__.py和user_id.py文件。編寫user_id.py的代碼:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import uuid
USER_KEY = 'uid'
TEN_YEARS = 60 * 60 * 24 * 365 * 10
class UserIDMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
uid = self.generate_uid(request)
request.uid = uid
response = self.get_response(request)
# max_age設置瀏覽器中存儲的cookie的有效時長,httponly=True設置該cookie只能在服務器端纔可以訪問
response.set_cookie(USER_KEY, uid, max_age=TEN_YEARS, httponly=True)
return response
def generate_uid(self, request):
try:
# print(request.COOKIES)
uid = request.COOKIES[USER_KEY]
# print('已存在的uid:', uid)
except KeyError:
# 生成唯一的用戶id
uid = uuid.uuid4().hex
# print('產生新的uid:', uid)
return uid
(3)把上面的middleware配置到settings.py文件中:
MIDDLEWARE = [
'APP的目錄名稱.middleware.user_id.UserIDMiddleware', # 產生用戶唯一id的middleware
# 省略其他代碼
]
特別說明: Django的middleware在項目啓動時會被初始化,等接受請求之後,Django會根據settings.py中的MIDDLEWARE 的配置順序來挨個調用,傳遞request作爲參數。
2、設置用戶的訪問記錄,並且把該記錄保存到緩存中。當用戶訪問某篇文章的時候,我們可以根據緩存中的記錄來統計文章的訪問次數。
注意: 在此,我們使用Django的緩存來存儲訪問記錄,但是Django的緩存在未配置的情況下,使用的是內存緩存。
(注:Django緩存在後端支持多種配置,如:memcache、MySQL,、文件系統、內存(默認)、Redis)
如果是單進程,這沒有問題;如果是多進程,就會出現問題,因爲內存緩存是進程間獨立的。因此,你要根據自己的業務來選擇如何配置Django的緩存。
我們把這一步的具體實現代碼放在文章詳情頁的視圖函數中,首先爲大家展示一下整個APP對應的視圖函數views.py文件:
""" class-based view """
from datetime import date
from django.core.cache import cache
from django.db.models import Q, F
from django.shortcuts import get_object_or_404
from django.views.generic import ListView, DetailView
from config.models import SideBar
from .models import Post, Tag, Category
# 公共view:獲取導航欄和側邊欄的數據
class CommonViewMixin:
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({
'sidebars': SideBar.get_all(),
})
context.update(Category.get_navs())
return context
# 文章詳情頁視圖函數PostDetailView
class PostDetailView(CommonViewMixin, DetailView):
queryset = Post.latest_posts()
template_name = 'blog/detail.html'
context_object_name = 'post' # 設置傳遞到模板文件的變量名稱
pk_url_kwarg = 'post_id' # 在DetailView中,會根據這個參數來過濾數據,如:post = queryset.filter(pk=post_id)
def get(self, request, *args, **kwargs):
response = super().get(request, *args, **kwargs)
self.handle_visited() # 更新文章的訪問次數
return response
def handle_visited(self):
increase_pv = False
uid = self.request.uid # 獲取middleware中設置的用戶id
pv_key = 'pv:%s:%s' % (uid, self.request.path)
if not cache.get(pv_key):
increase_pv = True
cache.set(pv_key, 1, 1*60) # 1分鐘有效,防止統計1分鐘內多次刷新的情況
# print('產生新的pv_key:', pv_key)
if increase_pv:
Post.objects.filter(pk=self.object.id).update(pv=F('pv') + 1)
代碼分析:
上面Post的pv字段存儲的是該篇文章的訪問次數。重點關注handle_visited函數:
(1)獲取middleware中設置的用戶id:
uid = self.request.uid
(2)記錄用戶訪問該篇文章的記錄:
pv_key = 'pv:%s:%s' % (uid, self.request.path)
(3)訪問緩存是否有用戶訪問該篇文章的記錄,True:不需要統計此次的訪問次數;False:需要統計此次的訪問次數,並且把訪問記錄寫到緩存中。緩存的有效時間爲1分鐘,這樣可以防止統計用戶在一分鐘內多次刷新該頁面的情況。
if not cache.get(pv_key):
increase_pv = True
cache.set(pv_key, 1, 1*60) # 1分鐘有效,防止統計1分鐘內多次刷新的情況
# print('產生新的pv_key:', pv_key)
if increase_pv:
Post.objects.filter(pk=self.object.id).update(pv=F('pv') + 1)
上面handle_visited函數的代碼邏輯應該挺清晰的。