实现方法:
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