1. 简单计数处理
1. Blog模型添加数字字段记录,每次有人打开,记录数+1
models.py
read_num = models.IntegerFeild(default=0)
admin.py
@admin.register(Articles)class ArticlesAdmin(admin.ModelAdmin):list_display = ('id', 'title', 'article_type','author','read_num','created_time','last_updated_time')
views.py的article_details()中 添加
article.read_num += 1 # 阅读数+1
article.save()
html文件中添加 Views: ({{ article.read_count }})

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

缺点:
1. 后台编辑会影响`read_num和`last_pudated_time`
2. 功能单一,无法统计某一天的阅读数
3. 技术功能独立与后台分离
删除models.py中Articles(models.Model)类中的read_num属性,对应删除admin.py中ArticlesAdmin(admin.ModelAdmin)的read_num属性。
新建models.py中ReadNum(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


4. 对任意模型计数——django.contrib.contexttypes
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

4. 简单计数方法的缺点
- 后台编辑博客可能影响数据
- 功能单一,无法统计某一天的阅读数
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


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>

