zoukankan      html  css  js  c++  java
  • 博客阅读计数优化

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

    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)
     




  • 相关阅读:
    sqlplus时报Linux-x86_64 Error: 13: Permission denied
    thrift之TTransport层的缓存传输类TBufferedTransport和缓冲基类TBufferBase
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 队列操作
    Java实现 蓝桥杯 算法提高 队列操作
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 合并石子
  • 原文地址:https://www.cnblogs.com/lag1/p/13832403.html
Copyright © 2011-2022 走看看