欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

从零开始的django开发生活之博客阅读计数优化(13)

程序员文章站 2022-04-24 09:47:59
...

十三、博客阅读计数优化

1、计数功能独立

博客内容与计数字段分离

  1. 方法一

在models中专门创建一个ReadNum模型,将之前Blog模型中的read_num字段删除,admin中也要删除

class ReadNum(models.Model):
    read_num = models.IntegerField(default=0)
    blog = models.OneToOneField(Blog, on_delete=models.DO_NOTHING)

在admin中作如下修改:

from .models import BlogType, Blog, ReadNum
@admin.register(ReadNum)
class ReadNumAdmin(admin.ModelAdmin):
    list_display = ('read_num', 'blog')

打开shell,查看blog中的方法,发现了readnum,发现可以直接调用readnum方法,这样会显示出ReadNum的一个对象,再对admin修改

从零开始的django开发生活之博客阅读计数优化(13)

@admin.register(Blog)
class BlogAdmin(admin.ModelAdmin):
    list_display = ('title', 'blog_type', 'author', 'readnum', 'created_time', 'last_updated_time')

从零开始的django开发生活之博客阅读计数优化(13)
我们想要显示的并不是对象,但是直接在admin中写readnum.read_num是不会识别的,所以在Blog模型中再加一个方法

def get_read_num(self):
        return self.readnum.read_num

admin中readnum改为get_read_num,后台管理界面就能显示出数量,而非对象了

下面在views中写自增算法

将原先的自增改为

 if not request.COOKIES.get('blog_%s_readed' % blog_pk):
        if ReadNum.objects.filter(blog=blog).count():
            readnum = ReadNum.objects.get(blog=blog)
        else:
            readnum = ReadNum(blog=blog)
        readnum.read_num += 1
        readnum.save()

前端页面相应部分调用Blog模型中的get_read_num方法即可。

在没有点到的博客部分阅读数部分是空白的,并没有显示0,只有点击一次之后才会显示1,怎么让他默认显示0呢,我们需要对get_read_num方法改进

先在shell模式试验
从零开始的django开发生活之博客阅读计数优化(13)

可以看到之所以没有任何显示,是因为没有点击到的对象,Blog和ReadNum没有关联到一起,抛出ObjectDoesNotExist错误,下面对get_read_num进行改进:

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

这样就能默认显示0了。

2、对任意模型计数

单独创建一个app用于各种计数,django中有一个框架ContentType用于记录所有模型,它是一个模型。先在shell模式下进行试验。
从零开始的django开发生活之博客阅读计数优化(13)
可以看到里面的确有我们先前创建的模型

下面删除之前所写的ReadNum模型以及与之相关的方法,转而采用现在的方法,首先创建一个app read_statistics,在models中写入

参考文档

参考文档中有contentype的用法,我们找到Generic relations,里面的代码直接可以引用

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)

    content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

content_type通过外键指向一个模型,object_id记录指向模型主键值,content_object将前面两个集成为一个通用外键

在settings中注册app,然后进行数据库迁移

admin定制后台:

from django.contrib import admin
from .models import ReadNum

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

查看后台,添加一条记录

从零开始的django开发生活之博客阅读计数优化(13)
接下来使得后台blog中也能看到read_num

先在shell模式下实验:
从零开始的django开发生活之博客阅读计数优化(13)

这里已经取出blog类型,id=36的1条ReadNum查询,下面将取出查询中的第一条记录也是目前为止的仅有记录
从零开始的django开发生活之博客阅读计数优化(13)
在blog/models写入方法:

def get_read_num(self):
        ct = ContentType.objects.get_for_model(self)
        try:
            readnum = ReadNum.objects.get(content_type=ct, object_id=self.pk)
            return readnum.read_num
        except exceptions.ObjectDoesNotExist as e:
            return 0

blog/views,blog_detail方法中写入:

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(blog=blog)
        else:
            #不存在记录则创建记录
            readnum = ReadNum(content_type=ct, object_id=blog.pk)
        readnum.read_num += 1
        readnum.save()

以上方法是为了通用性的要求,所以我们对其进行封装,以后可以直接调用,先对模型中的计数方法get_read_num进行封装

将此方法封装为read_statistics/models中的一个类

class ReadNumExpand():
    def get_read_num(self):
        ct = ContentType.objects.get_for_model(self)
        try:
            readnum = ReadNum.objects.get(content_type=ct, object_id=self.pk)
            return readnum.read_num
        except exceptions.ObjectDoesNotExist as e:
            return 0

再导入相关的库,blog/models中导入ReadNumExpand类,Blog继承ReadNumExpand类,封装完成

再对views中的方法封装

在read_statistic新建python文件utils.py,将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)
        readnum.read_num += 1
        readnum.save()
    return key

因为我们需要cookie的键名,所以返回key。ct.models应用了contenttype模型的一个属性,可以看文档有说明

在blog/views中导入Python文件 from read_statistics.utils import read_statistics_once_read

获取键名 read_cookie_key = read_statistics_once_read(request, blog)

替换原参数 response.set_cookie(read_cookie_key, 'true')

大功告成!

相关标签: 网站制作