Django實現文章訪問次數的統計

實現方法:

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函數的代碼邏輯應該挺清晰的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章