飞道的博客

Django实现文章访问次数的统计

396人阅读  评论(0)

实现方法:

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函数的代码逻辑应该挺清晰的。
转载:https://blog.csdn.net/guanmaoning/article/details/106864986
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场