[Django学习] Django基础(9)_阅读计数

一. 简单计数法

(一) 实施方法

  1. 在现有的models模型中添加readed_num字段用于记录阅读数据。

class Blog(models.Model):
	title = models.CharField(max_length=50)
	blog_type = models.ForeignKey(BlogType, on_delete=models.DO_NOTHING)
	content = RichTextUploadingField()
	author = models.ForeignKey(User, on_delete=models.DO_NOTHING)
	readed_num = models.IntegerField(default=0)

  2. 通过设定浏览器cookie规则,将用户访问时产生的cookie保存到本地;

  3. 每次访问时,读取cookie,若存在,readed_num+1

def blog_detail(request, blog_pk):
	""" render data to blog_detail.html """
	blog = get_object_or_404(Blog, pk=blog_pk)
        # 访问时,读取cookie文件进行判断
	if not request.COOKIES.get('blog_%s_readed' % blog_pk):
		blog.readed_num += 1
		blog.save()
	context = {}
	context['blog'] = blog
	context['previous_blog'] = Blog.objects.filter(create_time__gt=blog.create_time).last()
	context['next_blog']=Blog.objects.filter(create_time__lt=blog.create_time).first()
	response = render_to_response('blog/blog_detail.html', context)
        # 设定浏览器cookie存储规则
	response.set_cookie('blog_%s_readed' % blog_pk,'true')
	return response  

(二) 优点缺点

  1. 优点

    简单,易理解

  2. 缺点

    (1) 后台编辑时,若有人访问数据,产生的阅读计数被编辑时的数据覆盖

    (2) 功能单一,无法统计某一天的阅读数

二.  对任意模型计数

博客-|    |-关联哪个模型?
教程--计数-
其他-|    |-对应哪个对象?

  若相对任意模型计数,必须知道计数的方法关联哪个模型?对应哪个模型对象?  

三. ContentType--对特定模型及对象进行管理

(一) contenttypes framework

  Django包含一个contenttypes应用程序,它可以跟踪安装在Django驱动的项目中的所有模型,为使用模型提供高级的通用接口。

  ContentType应用程序的核心是ContentType模型,通过django.contrib.contenttypes.models.ContentType引用。ContentType实例表示并存储有关在项目中安装的模型的信息,每当安装新模型时,就自动创建ContentType的新实例。

  ContentType实例具有返回它们表示的模型类和从这些模型查询对象的方法。ContentType还有一个自定义管理器 ContentTypeManager,用于添加处理ContentType和获取特定模型的ContentType实例的方法。

  您的模型和ContentType之间的关系还可以用于在您的一个模型的实例和您安装的任何模型的实例之间启用“通用”关系。

(二) 使用ContentType计数

1. 创建统计应用blogstatistics

2. 创建统计模型ReadNum

   按照contenttypes framework的文档(如图所示)创建要统计模型。

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

# 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')

3. 在blog应用中引用blogstatistics应用创建的模型

class Blog(models.Model):
	title = models.CharField(max_length=50)
	blog_type = models.ForeignKey(BlogType, on_delete=models.DO_NOTHING)
	content = RichTextUploadingField()
	author = models.ForeignKey(User, on_delete=models.DO_NOTHING)
	create_time = models.DateTimeField(auto_now_add=True)
	last_update = models.DateTimeField(auto_now=True)

	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

	def __str__(self):
		return "<BLog: %s>" % self.title

	class Meta:
		ordering = ['-create_time']

4. 在blog.views.py中引用blogstatistics应用创建的模型 

def blog_detail(request, blog_pk):
	""" render data to blog_detail.html """
	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):
			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()

	context = {}
	context['blog'] = blog
	context['previous_blog'] = Blog.objects.filter(create_time__gt=blog.create_time).last()
	context['next_blog']=Blog.objects.filter(create_time__lt=blog.create_time).first()
	response = render_to_response('blog/blog_detail.html', context)
	response.set_cookie('blog_%s_readed' % blog_pk,'true')
	return response

5. 在模版页面中直接调用blog.models.Blog模型的方法get_read_num

<p class="blog-info">
    <span class="glyphicon glyphicon-tags"></span><a href="{% url 'blogs_with_type' blog.blog_type.pk %}">  {{ blog.blog_type }}</a>  
    <span class="glyphicon glyphicon-calendar"></span>  {{ blog.create_time|date:"Y-m-d" }}  <span class="glyphicon glyphicon-star-empty"></span>  阅读({{blog.get_read_num}})
</p>

四. 重复代码封装--实现通用性统计计数

  通过类的继承来实现类函数的继承调用

(一) 需要封装的地方(红色标记)

def blog_detail(request, blog_pk):
	""" render data to blog_detail.html """
	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):
			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()

	context = {}
	context['blog'] = blog
	context['previous_blog'] = Blog.objects.filter(create_time__gt=blog.create_time).last()
	context['next_blog']=Blog.objects.filter(create_time__lt=blog.create_time).first()
	response = render_to_response('blog/blog_detail.html', context)
	response.set_cookie('blog_%s_readed' % blog_pk,'true')
	return response
class Blog(models.Model):
	title = models.CharField(max_length=50)
	blog_type = models.ForeignKey(BlogType, on_delete=models.DO_NOTHING)
	content = RichTextUploadingField()
	author = models.ForeignKey(User, on_delete=models.DO_NOTHING)
	create_time = models.DateTimeField(auto_now_add=True)
	last_update = models.DateTimeField(auto_now=True)

	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

	def __str__(self):
		return "<BLog: %s>" % self.title

	class Meta:
		ordering = ['-create_time']

(二) 方法

1. 在blogstatistics.models中创建类,将get_read_num方法封装到该类中。

注意:get_read_num方法参数是self

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:
			return 0

2. 在blog.models中继承ReadNumExpandMethod类。Blog模型即可调用ReadNumExpandMethod.get_read_num(),且该方法调用的参数是self=Blog 

class Blog(models.Model, ReadNumExpandMethod):
	title = models.CharField(max_length=50)
	blog_type = models.ForeignKey(BlogType, on_delete=models.DO_NOTHING)
	content = RichTextUploadingField()
	author = models.ForeignKey(User, on_delete=models.DO_NOTHING)
	create_time = models.DateTimeField(auto_now_add=True)
	last_update = models.DateTimeField(auto_now=True)

	def __str__(self):
		return "<BLog: %s>" % self.title

	class Meta:
		ordering = ['-create_time']

3. 在blogstatistics应用中新建一个python文件utils.py,对blog.views.py中关于COOKIE的内容进行封装  

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

4. blog.views.py中blog_detail函数即可使用该封装的函数。

def blog_detail(request, blog_pk):
	""" render data to blog_detail.html """
	blog = get_object_or_404(Blog, pk=blog_pk)
	read_cookie_key = read_statistics_once_read(request, blog)

	context = {}
	context['blog'] = blog
	context['previous_blog'] = Blog.objects.filter(create_time__gt=blog.create_time).last()
	context['next_blog']=Blog.objects.filter(create_time__lt=blog.create_time).first()
	response = render_to_response('blog/blog_detail.html', context)
	response.set_cookie(read_cookie_key,'true')
	return response

注明:学习资料来自“再敲一行代码的个人空间”以及“杨仕航的博客”  

原文地址:https://www.cnblogs.com/AngryZe/p/9284771.html