博客阅读计数优化

为了解决上一篇写到的计数功能的单一,我们把把计数功能独立,把博客内容和计数字段分开,再通过外键关联

class Blog(models.Model):
title = models.CharField(max_length=50)
blog_type = models.ForeignKey(BlogType,on_delete=models.CASCADE)
content = RichTextUploadingField()
author = models.ForeignKey(User,on_delete=models.CASCADE)

created_time = models.DateTimeField(auto_now_add=True)
last_updated_time = models.DateTimeField(auto_now_add=True)

def __str__(self):
return '<Blog: %s>' %self.title

class Meta:
ordering = ['-created_time']

class ReadNum(models.Model):
read_num = models.IntegerField(default=0)
blog = models.OneToOneField(Blog,on_delete=models.CASCADE)
把原来Blog模型中的计数字段删除,新增ReadNum类,并于Blog一对一关联,再同步数据库
admin中显示
@admin.register(ReadNum)
class ReadNumAdmin(admin.ModelAdmin):
list_display = ('read_num','blog')
此时在admin后台修改博客时就不会影响到博客的计数。

为了在后台blogs中看到每篇博客自己的计数,可以在Blog模型写一个方法,然后admin中调用这个方法。模型中的方法在admin和前端可以调用。
因为是一对一关联,此时反向查询,基于对象,只需对象.类名小写得到目标对象
利用错误如果不存在记录则返回0,这里是不管什么错误,也可以导入错误的集合,错误的集合里有对象不存在的情况。
def get_read_num(self):
try:
return self.readnum.read_num
except Exception as e:
return 0

from django.db.models.fields import exceptions
def get_read_num(self):
try:
return self.readnum.read_num
except exceptions.ObjectDoesNotExist:
return 0

在前端显示的需要修改vires和前端页面,views中计数+1因为博客和计数的模型分离,需要判断对应的记录是否存在
if ReadNum.objects.filter(blog=blog).count(): #判断这条博客对应的计数模型有没有
#存在记录,获取这个计数对象
readnum = ReadNum.objects.get(blog=blog)
else:
#不存在对应的记录,创建
readnum = ReadNum(blog=blog)
#计数加1
readnum.read_num +=1
readnum.save()


2.可以对任意模型计数
计数——>博客、教程、公告、其他等等 都需要进行阅读的计数,如果我们要对一个新的模型进行计数,此时计数是一对一绑定的,那么只能再写一次代码,每多一个需要计数的,
就需要多创建一个模型。
可以让计数模型——>关联哪个模型、对应主键值,这样一个模型可以统计各种模型的数据——>Django中有ContentType自动把这些东西记录下来,它记录了我们这个项目所有的模型,

>>> from django.contrib.contenttypes.models import ContentType>>> ContentType

<class 'django.contrib.contenttypes.models.ContentType'>
>>> ContentType.objects.all()
<QuerySet [<ContentType: log entry>, <ContentType: group>, <ContentType: permission>, <ContentType: user>, <ContentType: blog>, <ContentType: blog type>, <Conten
tType: read num>, <ContentType: content type>, <ContentType: session>]>

删除原有计数模型,建立一个read_statistics app

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class ReadNum(models.Model):
read_num = models.IntegerField(default=0)

#将您的模型ForeignKey 设为ContentType 通过ContentType找到具体的模型
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField() #记录对应模型的主键值 该字段可以存储您将要关联的模型中的主键值
#给您的模型一个 GenericForeignKey,并为其传递上述两个字段的名称。如果将这些字段分别命名
# 为“ content_type”和“ object_id”,则可以忽略这些-这些是默认字段名称 GenericForeignKey。
content_object = GenericForeignKey('content_type', 'object_id')

在admin中注册显示
from django.contrib import admin
from .models import ReadNum

@admin.register(ReadNum)
class ReadNumAdmin(admin.ModelAdmin):
list_display = ('read_num','content_object')

添加一条记录后,在shell中调试

>>> from read_statistics.models import ReadNum
>>> from blog.models import Blog
>>> from django.contrib.contenttypes.models import ContentType
>>> ContentType.objects.get_for_model(Blog)
<ContentType: blog>
>>> ct = ContentType.objects.get_for_model(Blog)
>>> blog = Blog.objects.first()
>>> blog
<Blog: <Blog: for 30>>
>>> blog.pk
36
>>> ReadNum.objects.filter(content_type=ct,object_id=blog.pk)
<QuerySet [<ReadNum: ReadNum object (1)>]>
>>> rn = ReadNum.objects.filter(content_type=ct,object_id=blog.pk)[0]
>>> rn
<ReadNum: ReadNum object (1)>
>>> rn.read_num
10

根据shell获取数据的方法在博客模型中重写获取博客阅读数量的方法

def get_read_num(self):
try:
ct = ContentType.objects.get_for_model(self) #获取模型类或模型实例,然后返回ContentType代表该模型的实例 <ContentType: blog>
readnum = ReadNum.objects.get(content_type=ct, object_id=self.pk)
return readnum.read_num
except exceptions.ObjectDoesNotExist:
return 0

然后修改下views中的计数规则
def blog_detail(request, blog_pk):
blog = get_object_or_404(Blog, pk=blog_pk)
if not request.COOKIES.get('blog_%s_readed' % blog_pk):
ct = ContentType.objects.get_for_model(Blog)
if ReadNum.objects.filter(content_type=ct, object_id=blog.pk).count(): #判断这条博客对应的计数模型有没有
#存在记录,获取这个计数对象
readnum = ReadNum.objects.get(content_type=ct, object_id=blog.pk)
else:
#不存在对应的记录,创建
readnum = ReadNum(content_type=ct, object_id=blog.pk)
#计数加1
readnum.read_num +=1
readnum.save()
此时就可以了,但是还可以进行通用性处理,把该封装的代码封装到计数app里面。通过类的继承,把博客的获取阅读数量方法放到计数模型中,并创建一个类,
之后让博客模型继承这个类。
class ReadNumExpandMethod():
def get_read_num(self):
try:
ct = ContentType.objects.get_for_model(self) #获取模型类或模型实例,然后返回ContentType代表该模型的实例
readnum = ReadNum.objects.get(content_type=ct, object_id=self.pk)
return readnum.read_num
except exceptions.ObjectDoesNotExist:
return 0
把views中的计数规则放到计数app的工具文件中写成一个方法,让views直接调用方法
from django.contrib.contenttypes.models import ContentType
from .models import ReadNum

def read_statistics_once_read(request, obj):
ct = ContentType.objects.get_for_model(obj)
key = '%s_%s_read' % (ct.model, obj.pk)
if not request.COOKIES.get(key):
if ReadNum.objects.filter(content_type=ct, object_id=obj.pk).count(): # 判断这条博客对应的计数模型有没有
# 存在记录,获取这个计数对象
readnum = ReadNum.objects.get(content_type=ct, object_id=obj.pk)
else:
# 不存在对应的记录,创建
readnum = ReadNum(content_type=ct, object_id=obj.pk)
# 计数加1
readnum.read_num +=1
readnum.save()
return key

可以传过来一个对象,通过对象获取模型类或模型实例,然后返回ContentType代表该模型的实例,获取主键,

>>> ct
<ContentType: blog>
>>> ct.model
'blog'

最后返回cookie的键

在博客的视图中调用该方法

read_cookie_key = read_statistics_once_read(request, blog)
 




原文地址:https://www.cnblogs.com/lag1/p/13832403.html