我的博客开发(012)

优化上面的两个缺点:

优化缺点一:后台编辑博客可能影响数据

将博客内容和计数字段分开,即按照ACID原则进行设计:将模型重新分类
我的博客开发(012)_第1张图片
python manage.py makemigrations;

python manage.py migrate;

然后admin.py中新建一个阅读数模型并注册
我的博客开发(012)_第2张图片
代码如下:先修改views.py:

...
from .models import Blog, BlogType,ReadNum
...

...
def blog_detail(request, blog_pk):
    """
      打开某一个博文后的显示内容
      """
    blog = get_object_or_404(Blog, pk=blog_pk)


    # 因为没有设置set_cookie的过期时间,所以默认是当关闭浏览器时删除cookie,所以刷新失效,只有重新打开才有效!
    if not request.COOKIES.get('blog_%s_readed' % blog_pk):
        # blog.readed_num += 1
        # blog.save()
        if ReadNum.objects.filter(blog=blog).count():  # 如果有阅读量则获取数量
            readnum = ReadNum.objects.get(blog=blog)
        else:  # 如果没有阅读量则创建对象,处理异常
            readnum = ReadNum()
            readnum.blog = blog
        # 计数加一
        readnum.read_num += 1
        readnum.save()
...

修改blog_detail.html

  • 阅读量:{{ blog.get_read_num }}
  • 修改blog_list.html

    阅读量:{{ blog.get_read_num }}
    

    修改命名,修改部分read_num为get_read_num
    新问题:现在的detail_list中的阅读量如果没有值会返回空,应该要返回零
    我的博客开发(012)_第3张图片
    修改如下:处理异常

    # views.py
    class Blog(models.Model):
        ...
        def get_read_num(self):
        ...
        try:
            return self.readnum.read_num
        except Exception as e:
            return 0
        ...

    我的博客开发(012)_第4张图片
    为了增强代码健壮性,能够处理各种bug,修改代码如下:

    # views.py
    from django.db.models.fields import exceptions
    ...
    
    class Blog(models.Model):
        def get_read_num(self):
            """
            引导调用ReadNum中的read_num方法
            """
            try:
                return self.readnum.read_num
            except exceptions.ObjectDoesNotExist:
                return 0

    功能要求:我们封装的计数功能呢可以被调用从而对任何模型进行计数:使用ContentType,并将此模型封装成一个app来使用
    我的博客开发(012)_第5张图片
    为了使用新的contenttype来代替自己定义的计数器,先将该模型类以及相关引用注释起来

    models.py

    class Blog(models.Model):
       ......
    
        '''
        # models.py中的get_read_num方法去掉
        def get_read_num(self):
            """
            引导调用ReadNum中的read_num方法
            """
            try:
                return self.readnum.read_num
            except exceptions.ObjectDoesNotExist:
                return 0
        '''
    
      ......
    
    '''
    # 为了使用新的contenttype来代替自己定义的计数器,先将该模型类注释起来
    class ReadNum(models.Model):
        """
        计数字段,优化后台编辑博客可能影响数据缺点
        """
        read_num = models.IntegerField(default=0)
        # 将计数字段与博客模型相关联,当删除博文时不要对博客的计数字段做任何事情
        # 外键:一对多或者多对一使用,如果是一对一使用OneToOneField;这里因为是一个字段对应一个博文,所以是一对一
        blog = models.OneToOneField(Blog,on_delete=models.DO_NOTHING)
    '''

    然后admin中的

    from django.contrib import admin
    from .models import BlogType,Blog  # 去掉ReadNum
    ......
    
    @admin.register(Blog)
    class BlogAdmin(admin.ModelAdmin):
        list_display = ('title','blog_type','author','created_time','last_updated_time')
    
    
    '''
    @admin.register(ReadNum)
    class ReadNumAdmin(admin.ModelAdmin):
        list_display = ('read_num','blog')
    '''

    views.py中

    from .models import Blog, BlogType  # 去掉ReadNum
    ...
        '''
    def get_blogs_list_common_data(request, blogs_all_list):
            # 加上省略页码标记
        if page_range[0] - 1 >= 2:
            page_range.insert(0, '...')  # 0表示位置,后面的省略号表示要插入的内容
        if paginator.num_pages - page_range[-1] >= 2:
            page_range.append('...')  # 使用append来将省略号插入到页码的最后
        # 加上首页和尾页
        if page_range[0] != 1:
            page_range.insert(0, 1)
        if page_range[-1] != paginator.num_pages:
            page_range.append(paginator.num_pages)
        '''
        ......
    def blog_detail(request, blog_pk):
        """
          打开某一个博文后的显示内容
          """
        blog = get_object_or_404(Blog, pk=blog_pk)
    
    
        # 因为没有设置set_cookie的过期时间,所以默认是当关闭浏览器时删除cookie,所以刷新失效,只有重新打开才有效!
        if not request.COOKIES.get('blog_%s_readed' % blog_pk):
            """
            # blog.readed_num += 1
            # blog.save()
            if ReadNum.objects.filter(blog=blog).count():  # 如果有阅读量则获取数量
                readnum = ReadNum.objects.get(blog=blog)
            else:  # 如果没有阅读量则创建对象,处理异常
                readnum = ReadNum()
                readnum.blog = blog
            # 计数加一
            readnum.read_num += 1
            readnum.save()
            """
            pass
    ......

    然后python manage.py runserver来运行服务器检查是否有漏删错删的,如果正常则正式开始创建app:

    创建一个app:python manage.py startapp read_statistics

    read_statistics中的models中新建一个模型,然后关联内置模块ContentType:

    # read_statistics/models.py
    from django.db import models
    from django.contrib.contenttypes.fields import GenericForeignKey
    from django.contrib.contenttypes.models import ContentType
    
    
    # Create your models here.
    class ReadNum(models.Model):
        read_num = models.IntegerField(default=0)  # 规定read_num的类型为整型,如果不指定默认为0
        # 创建两个属性分别为content_type和object_id,前者外键关联ContentType app(django自带)
        content_type = models.ForeignKey(ContentType,on_delete=models.DO_NOTHING)
        object_id = models.PositiveIntegerField()  # object_id类型为自动增加的整型数字类型
        content_object = GenericForeignKey('content_type','object_id')  # 然后将上面两个属性变成一个通用的外键

    注册应用:setting

    # mysite/settings.py
    ......
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'ckeditor',
        'ckeditor_uploader',
        'blog',
        'read_statistics',
    ]
    ......

    python manage.py makemigrations

    python manage.py migrate

    admin中的写要显示的内容:

    # read_statistics/admin.py
    
    from django.contrib import admin
    from .models import ReadNum
    
    
    # Register your models here.
    @admin.register(ReadNum)
    class ReadNumAdmin(admin.ModelAdmin):
        """
        计数app功能
        """
        list_display = ('read_num','content_object')

    python manage.py runserver

    效果如图:
    我的博客开发(012)_第6张图片
    那么现在如何能够通过上面的计数app中的ReadNum模型来获取里面的Read num数量然后展示在blog_list.html和blog_detail.html中呢?方法如下:

    先尝试通过shell模式来获取ContentType内容

    python manage.py shell
    from read_statistics.models import ReadNum
    from blog.models import Blog
    from django.contrib.contenttypes.models import ContentType
    ContentType.objects.filter(model='blog')
    ContentType.objects.get_for_model(Blog)
    ct = ContentType.objects.get_for_model(Blog)
    ct
    # 此时的ct就是要提取的ContentType数据
    # 在提取主键值:
    blog = Blog.objects.first()
    blog.pk
    # 最后通过上面两个参数拿取对应的数据
    ReadNum.objects.filter(content_type=ct,object_id=blog.pk)
    # 具体查询其里面的内容:
    rn = ReadNum.objects.filter(content_type=ct,object_id=blog.pk)[0]
    rn
    rn.read_num
    quit()

    blog/models.py

    
    from django.contrib.contenttypes.models import ContentType
    from read_statistics.models import ReadNum
    ...
    
    class Blog(models.Model):
        ...
        def get_read_num(self):
        """
        用来获取read_statistics中的计数数据的方法
        """
            ct = ContentType.objects.get_for_model(Blog)  # 获取contenttype:blog|blog
            # 获取对应的计数值
            readnum = ReadNum.objects.get(content_type=ct,object_id=self.pk)
            return readnum.read_num
        ...

    blog/admin.py
    image.png
    python manag.py runserver

    效果如下:
    我的博客开发(012)_第7张图片
    但是10下面不存在显示的是-而不是0,使用try-except来处理异常

    # blog/models.py
    
    ...
    class Blog(models.Model):
        ...
        def get_read_num(self):
        """
        用来获取read_statistics中的计数数据的方法
        """
            try:
                ct = ContentType.objects.get_for_model(Blog)
                readnum = ReadNum.objects.get(content_type=ct, object_id=self.pk)
                return readnum.read_num
            except exceptions.ObjectDoesNotExist:
                return 0

    image.png
    修改视图函数:blog/views.py

    from django.contrib.contenttypes.models import ContentType
    from read_statistics.models import ReadNum
    ......
    
    def blog_detail(request, blog_pk):
        """
      打开某一个博文后的显示内容
      """
    blog = get_object_or_404(Blog, pk=blog_pk)
    
    
    # 因为没有设置set_cookie的过期时间,所以默认是当关闭浏览器时删除cookie,所以刷新失效,只有重新打开才有效!
    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)
        # 计数加一
        readnum.read_num += 1
        readnum.save()
        ......

    效果如下:
    我的博客开发(012)_第8张图片
    现在blog/models.py中的Blog还是太臃肿了,想要把get_read_num封装出来做成一个父类,然后让Blog类来继承get_read_num,就可以精简,代码如下:

    # read_statistics/models.py
    from django.db.models.fields import exceptions  # 里面包含各种错误
    ......
    class ReadNumExpandMethod():
        """
        用来封装read_statistics中的计数方法
        """
        def get_read_num(self):
            try:
                ct = ContentType.objects.get_for_model(self)
                readnum = ReadNum.objects.get(content_type=ct, object_id=self.pk)
                return readnum.read_num
            except exceptions.ObjectDoesNotExist:
                return 0
    
    class Blog(models.Model,Test):
    ......
    # blog/models.py
    from read_statistics.models import ReadNumExpandMethod
    ......
    
    class Blog(models.Model,ReadNumExpandMethod):
        ......

    没有bug,真开心

    然后同样将blog/views.py中的blog_detail中的判断内容封装在read_statistics/utils.py中:

    新建utils.py

    # read_statistics/utils.py
    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)
        # 因为没有设置set_cookie的过期时间,所以默认是当关闭浏览器时删除cookie,所以刷新失效,只有重新打开才有效!
        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
    # blog/views.py
    from read_statistics.utils import read_statistics_once_read
    ......
    
    def blog_detail(request, blog_pk):
        read_cookie_key = read_statistics_once_read(request, blog)
        ......
        response.set_cookie(read_cookie_key,'true')
        ...

    搞定

    你可能感兴趣的:(python,django,javascript,前端,数据库)