1. 简单计数处理

1. Blog模型添加数字字段记录,每次有人打开,记录数+1

models.py

  1. read_num = models.IntegerFeild(default=0)

admin.py

  1. @admin.register(Articles)
  2. class ArticlesAdmin(admin.ModelAdmin):
  3. list_display = ('id', 'title', 'article_type',
  4. 'author',
  5. 'read_num',
  6. 'created_time',
  7. 'last_updated_time')

views.pyarticle_details()中 添加

article.read_num += 1 #   阅读数+1
article.save()

html文件中添加 Views: ({{ article.read_count }})

16 博文阅读计数 - 图1

2. 自定义技术规则

可规定阅读一次的概念:

    1.    无视是否同一个人,每次打开都记录
    2.    若同一个人,每隔多久打开一次才阅读数+1

views.py中的article_details()

    if not request.COOKIES.get('article_%s_read' % art_pk):
        # 当浏览器中该cookie不存在时 阅读加1.
        article.read_num += 1 #   阅读数+1
        article.save()    # 后台更新 ,会修改last_updated_time
        ...
    response = render(request, 'article_details.html', context)  #   响应
    response.set_cookie(key='article_%s_read'% art_pk,value='true',max_age=1800) # cookie提交给服务器,max_age : cookie有效时长,过期刷新cookie,单位为s
    return response

16 博文阅读计数 - 图2

缺点:

        1. 后台编辑会影响`read_num和`last_pudated_time`
                    2. 功能单一,无法统计某一天的阅读数

3. 技术功能独立与后台分离

删除models.pyArticles(models.Model)类中的read_num属性,对应删除admin.pyArticlesAdmin(admin.ModelAdmin)read_num属性。

新建models.pyReadNum(models.Model):

class Articles(models.Model):

    ...
    def get_read_num(self):
        try:
            return self.readnum.read_num
        except exceptions.ObjectDoesNotExist as e:
            return 0    # 对象的阅读记录不存在 返回0
    ...

class ReadNum(models.Model):
    read_num = models.IntegerField(default=0)
    article = models.OneToOneField(Articles, on_delete=models.DO_NOTHING)   #   read和article一对一

admin.py中注册ReadNumAdmin(admin.ModelAdmin):

from .models import ReadNum   

@admin.register(Articles)
class ArticlesAdmin(admin.ModelAdmin):
    list_display = (...
                    'get_read_num',
                    ... )


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

views.py中的article_details()

 if not request.COOKIES.get('article_%s_read' % art_pk):
        # 当浏览器中cookie不存在时 阅读加1.
        if ReadNum.objects.filter(article=article).count():
            # 存在记录
            readnum = ReadNum.objects.get(article=article)
        else:
            # 不存在记录
            readnum = ReadNum(article=article)
        readnum.read_num += 1  # 阅读数+1
        readnum.save()

        ...

         response = render(request, 'article_details.html', context)  #   响应
    # cookie提交给服务器,max_age : cookie有效时长,过期刷新cookie,单位为s
    response.set_cookie(key='article_%s_read' %
                        art_pk, value='true', max_age=1800)
    return response

html 界面添加article.get_read_num

16 博文阅读计数 - 图3

16 博文阅读计数 - 图4

4. 对任意模型计数——django.contrib.contexttypes

16 博文阅读计数 - 图5

1. python manage.py startapp read_statistics 新建一个用于计数的模块

read_statistics/models.py

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

# Create your models here.
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')

class ReadNumExpandMethod():
    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 as e:
            return 0

read_statistics/admin.py

from django.contrib import admin
from .models import ReadNum
# Register your models here.

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

read_statistics/utils.py 用于保存供其他app调用的公用方法

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


def get_read_statistics(request, obj):
    ct = ContentType.objects.get_for_model(obj)
    key = "%s_%s_read" % (ct.model, obj.pk)
    if not request.COOKIES.get(key):
        # 当浏览器中cookie不存在时 阅读加1.
        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  # 阅读数+1
        readnum.save()
    return key

2. s2aclab中调用read_statistics中的模块和方法

s2aclab/models.py

...
from django.contrib.contenttypes.models import ContentType
from read_statistics.models import ReadNumExpandMethod
...

class Articles(models.Model, ReadNumExpandMethod):
    ...

...

s2aclab/admin.py

@admin.register(Articles)
class ArticlesAdmin(admin.ModelAdmin):
    list_display = ('id', 'title', 'article_type',
                    'author',
                    'get_read_num',
                    'created_time',
                    'last_updated_time')
    #删除之前对于该app内ReadNum模块的注册
'''
@admin.register(ReadNum)
class ReadNumAdmin(admin.ModelAdmin):
    list_display = ('read_num','article')
'''

s2aclab/views.py

...
from read_statistics.models import ReadNum
from read_statistics.utils import get_read_statistics
...
def article_details(request, art_pk):
    context = {}
    article = get_object_or_404(Articles, pk=art_pk)
    read_cookie_key = get_read_statistics(request=request,obj=article)

    context['article'] = article
    context['next_article'] = Articles.objects.filter(
        created_time__gt=article.created_time).last()  # .last()表示取最后一条
    context['previous_article'] = Articles.objects.filter(
        created_time__lt=article.created_time).first()    # .first()表示取第一条
    response = render(request, 'article_details.html', context)  #   响应
    # cookie提交给服务器,max_age : cookie有效时长,过期刷新cookie,单位为s
    response.set_cookie(read_cookie_key, value='true', max_age=600)
    return response

16 博文阅读计数 - 图6

4. 简单计数方法的缺点

  1. 后台编辑博客可能影响数据
  2. 功能单一,无法统计某一天的阅读数

red_statistics/models.py新建类ReadDetails(models.Model)
class ReadDetails(models.Model):
    date = models.DateField(default=timezone.now)
    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')

.get_or_create()方法阅读数+1

read_statistics/utils.py修改get_read_statistics(request,obj)

from .models import ReadNum, ReadDetails 
...

# 获取总阅读数据 和一天的月度数据
def get_read_statistics(request, obj):
    ct = ContentType.objects.get_for_model(obj)
    key = "%s_%s_read" % (ct.model, obj.pk)
    if not request.COOKIES.get(key):
        # # 当浏览器中cookie不存在时 阅读加1.
        # 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,created_flag = ReadNum.objects.get_or_create(content_type=ct, object_id=obj.pk)    # get_or_create()返回元组
        readnum.read_num += 1  # 阅读数+1
        readnum.save()

        #当天阅读数+1
        date = timezone.now().date()
        readdetails, created_flag = ReadDetails.objects.get_or_create(content_type=ct, object_id=obj.pk, date=date)
        readdetails.read_num += 1
        readdetails.save()

    return key

新建get_one_week_read_statistics(content_type)

from django.db.models import Sum
from django.utils import timezone
from datetime import datetime,timedelta
from .models import ReadNum, ReadDetails 
...

# 获取一周内的流量数据
def get_one_week_read_statistics(content_type):
    today = timezone.now().date()
    dates = []
    read_sum = []
    for i in range(6, -1,-1):
        date = today + timedelta(days= -i)
        dates.append(date.strftime('%m/%d'))
        readdetails = ReadDetails.objects.filter(content_type=content_type, date=date)
        res = readdetails.aggregate(read_num_sum=Sum('read_num'))    # 返回字典{'read_num_sum':?}
        read_sum.append(res['read_num_sum'] or 0)
    return read_sum,dates

16 博文阅读计数 - 图7

16 博文阅读计数 - 图8

project/views.py
from django.contrib.contenttypes.models import ContentType
from read_statistics.utils import get_one_week_read_statistics
from s2aclab.models import Articles
from django.shortcuts import render,get_object_or_404

def home(request):
    content_type = ContentType.objects.get_for_model(Articles)
    read_sum,dates = get_one_week_read_statistics(content_type)

    context = {}
    context['read_sum'] = read_sum
    context['dates'] = dates
    return render(request,'home.html',context)

修改home.html echarts画图
<!-- echarts  -->
<div>
    <h3 class="home_content">Welcome to S2AC home! </h3>
</div>
<div id="area-chart" style="">
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-gl/dist/echarts-gl.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts-stat/dist/ecStat.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/dataTool.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/china.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/map/js/world.js"></script>
    <script type="text/javascript"
            src="https://api.map.baidu.com/api?v=2.0&ak=xfhhaTThl11qYVrqLZii6w8qE5ggnhrY&__ec_v__=20190126"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/echarts/dist/extension/bmap.min.js"></script>
    <script type="text/javascript">
            var dom = document.getElementById("area-chart");
            var myChart = echarts.init(dom);
            var app = {};
            option = null;
            option = {
                title: {
                    subtext: 'Views in last week',
                    left: 'center',
                    bottom:'top',
                    color: '#0eb0c9',
                },
                tooltip: {
                    trigger: 'axis',
                    axisPointer: {
                        type: 'cross',
                        label: {

                            backgroundColor: '#0eb0c9'
                        }
                    }
                },
                xAxis: {
                    type: 'category',
                    boundaryGap: false,
                    data: {{dates|safe}}
                },
                yAxis: {
                    type: 'value'
                },
                series: [{
                    name: 'daily visits',
                    data: {{ read_sum|safe }},
                    type: 'line',
                    lineStyle: {
                        color: '#0eb0c9'
                    },
                    areaStyle: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                            offset: 0,
                            color: '#c6e6e8'
                        }, {
                            offset: 1,
                            color: '#93d5dc'
                        }])
                    },
                }]
            };
            ;
            if (option && typeof option === "object") {
                myChart.setOption(option, true);
            }

    </script>
</div>

16 博文阅读计数 - 图9