优化以上两个缺点:优化第一个缺点:后台编辑博客可能会影响数据将博客内容和计数字段分开,即按照ACID原则设计:重新分类模型具体操作:修改models.py:先去掉读入admin\_numpythonmanage.pymakemigrations;python管理.py迁移;然后在admin.py中创建一个阅读数模型并注册。从这里修改read_num不会影响对应的最后更新时间:但是admin中缺少readnum列显示阅读量,改进处理如下:因为read\_num的值已经写入数据库,所以只需要读取,而且因为read\_num不在blog模型中,而是在另一个模型类readnum中,如果直接在admin中使用BlogAdmin读取另一个类中的方法,将无法读取它。如果写readnum.read\_num,也是功能限制,所以需要在models.py中的blog类中增加一个新的函数:read\_num来引导调用这个方法:然后修改admin中的BlogAdmin类。py:效果如下:另外blog\_list和blog\_detail的阅读数据没有获取到!代码如下:首先修改views.py:...from.modelsimportBlog,BlogType,ReadNum...defblog_detail(request,blog_pk):"""打开某篇博文后显示的内容"""blog=get_object_or_404(Blog,pk=blog_pk)#因为没有设置set_cookie的过期时间,默认是关闭浏览器时删除cookie,所以刷新无效,重新打开才有效!ifnotrequest.COOKIES.get('blog_%s_readed'%blog_pk):#blog.readed_num+=1#blog.save()ifReadNum.objects.filter(blog=blog).count():#Ifread获取数量readnum=ReadNum.objects.get(blog=blog)else:#如果没有阅读量,创建对象并处理异常readnum=ReadNum()readnum.blog=blog#countplus1readnum.read_num+=1readnum.save()...修改blog\_detail.html
阅读量:{{blog.get_read_num}}修改blog\_list.html阅读量:{{blog.get_read_num}}修改名称,修改部分read_num为get_read_num新问题:现在如果detail_list中的readingamount没有值,会返回空,应该返回0。修改如下:handleexception#views.pyclassBlog(models.Model):...defget_read_num(self):...try:returnself.readnum.read_numexceptExceptionase:return0...为了增强代码的健壮性和处理各种bug,修改代码如下:#views.pyfromdjango.db.models.fieldsimportexceptions...classBlog(models.Model):DEFGET_READ_NUM(Self):""Guide调用Readnum中的read_num方法""TRY:ReturnSelf.readnum.read_numExceptsnotexist:Return0function:Countanymodel:使用ContentType,将这个model封装成一个app要使用为了使用新的contenttype而不是自己定义的counter,首先注解model类和相关的引用models.pyclassBlog(models.Model):......'''#去掉models.py中的get_read_num方法defget_read_num(self):"""ReadNum中调用read_num方法的指南"""urtreself.readnum.read_numexceptexceptions.ObjectDoesNotExist:return0return'''......'''#为了使用新的contenttype而不是自己定义的计数器,首先注解模型类classReadNum(models.Model):"""计数字段,优化后台编辑博客可能影响数据的不足"""read_num=models.IntegerField(default=0)#将计数字段与博客模型相关联,删除博文时,do不对博客的计数字段做任何操作Anything#外键:一对多或多对一使用,如果是一对一使用OneToOneField;这里因为一个字段对应一篇博文,是一对一的blog=models.OneToOneField(Blog,on_delete=models.DO_NOTHING)'''thenfromdjango.contribimportadminfrom.modelsimportBlogType,Bloginadmin#删除ReadNum......@admin.register(Blog)classBlogAdmin(admin.ModelAdmin):list_display=('title','blog_type','author','created_time','last_updated_time')'''@admin.register(ReadNum)classReadNumAdmin(admin.ModelAdmin):list_display=('read_num','blog')'''viewfrom.modelsimportBlog,BlogTypeins.py#RemoveReadNum...'''defget_blogs_list_common_data(request,blogs_all_list):#添加省略页码标记ifpage_range[0]-1>=2:page_range.insert(0,'...')#0表示位置,后面的省略号表示要插入的内容ifpaginator.num_pages-page_range[-1]>=2:page_range.append('...')#使用append在页码末尾添加省略号Insert#添加第一页和最后一页ifpage_range[0]!=1:page_range.insert(0,1)ifpage_range[-1]!=paginator.num_pages:page_range.append(paginator.num_pages)'''......defblog_detail(request,blog_pk):"""打开某个博文后显示内容"""blog=get_object_or_404(Blog,pk=blog_pk)#因为set_cookie的过期时间没有设置,所以默认是关闭浏览器删除cookie,所以刷新无效,重新打开才有效!如果不是request.COOKIES.get('blog_%s_readed'%blog_pk):"""#blog.readed_num+=1#blog.save()ifReadNum.objects.filter(blog=blog).count():#如果有阅读量,获取个数readnum=ReadNum.objects.get(blog=blog)else:#如果没有阅读量如果数量大,创建对象并处理异常readnum=ReadNum()readnum.blog=blog#计数加一readnum.read_num+=1readnum.save()"""pass...然后运行pythonmanage.pyserver服务器检查是否有漏删或错删,如果正常则正式创建app:在新模型中的models中创建一个app:pythonmanage.pystartappread\_statisticsread\_statistics,然后关联内置模块ContentType:#read_statistics/models.pyfromdjango.dbimportmodelsfromdjango.contrib.contenttypes.fieldsimportGenericForeignKeyfromdjango.contrib.contenttypes.modelsimportContentType#Createyourmodelshere.classReadNum(models.Model):read_num=models.IntegerField(default#ault=0)规定read_num的类型为整数,不指定则默认为0#创建两个属性,分别是content_type和object_id,前者外键关联ContentType应用程序(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',]......pythonmanage.pymakemigrations写成pythonmanage.pymigrateadmin显示内容:#read_statistics/admin.pyfromdjango.contribimportadminfrom.modelsimportReadNum#在这里注册你的模型。@admin.register(ReadNum)classReadNumAdmin(admin.ModelAdmin):"""countappfunction"""list_display=('read_num','content_object')pythonmanage.pyrunserver效果如图:添加对象id时发现没有对象id,需要自己找:然后发现id在博客中没有显示,所以BlogAdmin在blog/admin.py中的List\_display中添加id项:效果如图:操作如下:那么现在如何获取Rea的编号通过ReadNum模型在上面的countingapp中dnum然后显示在blog\_list.html和blog\_detail.html在哪里?方法如下:先尝试通过shell方式获取ContentType内容pythonmanage.pyshellfromread_statistics.modelsimportReadNumfromblog.modelsimportBlogfromdjango.contrib.contenttypes.modelsimportContentTypeContentType.objects.filter(model='blog')ContentType.objects.get_for_model(Blog)ct=ContentType.objects.get_for_model(Blog)ct#此时的ct就是要的ContentType被提取的Data#就是提取主键值: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]rnrn.read_numquit()blog/models.pyfromdjango.contrib.contenttypes.modelsimportContentTypefromread_statistics.modelsimportReadNum...classBlog(models.Model):...defget_read_num(self):"""read_statistics中用于获取count数据的方法"""ct=ContentType.objects.get_for_model(Blog)#获取contenttype:blog|blog#获取对应的计数值readnum=ReadNum.objects.get(content_type=ct,object_id=self.pk)returnreadnum.read_num...blog/admin.pythonmanag.pyrunserver效果如下:但是10以下不显示yes-而不是0,使用try-except来处理异常#blog/models.py...classBlog(models.Model):...defget_read_num(self):"""read_statistics中用于获取计数数据的方法"""try:ct=ContentType.objects.get_for_model(Blog)obreadsnumj=ReadNum.get(content_type=ct,object_id=self.pk)返回read_num.read_num除了异常。ObjectDoesNotExist:返回0修改图像数:blog/views.pyfromdjango.contrib.contenttypes.modelsimportContentTypefromread_statistics....modelsimport..defblog_detail(request,blog_pk):"""打开某篇博文后显示的内容"""blog=get_object_or_404(Blog,pk=blog_pk)#因为没有设置set_cookie的过期时间,所以默认是当browserisclosed浏览器打开时cookie被删除,所以刷新无效,重新打开才有效!如果不是request.COOKIES.get('blog_%s_readed'%blog_pk):ct=ContentType.objects.get_for_model(Blog)ifReadNum.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)#count加1readnum.read_num+=1readnum.save()......效果如下:现在blog/models.py中的Blog还是太臃肿了,想要封装get\_read\_num作为父类,然后让Blog类继承get\_read\_num,这样可以简化。代码如下:#read_statistics/models.pyfromdjango.db.models.fieldsimportexceptions#里面包含各种错误...classReadNumExpandMethod():"""用于封装read_statistics中的计数方法"""defget_read_num(self):尝试:ct=ContentType.objects.get_for_model(self)readnum=ReadNum.objects.get(content_type=ct,object_id=self.pk)returnreadnum.read_numexceptions.ObjectDoesNotExist:return0classBlog(models.Model,Test):......#blog/models.pyfrom.modelsread_statimportReadNumExpandMethod......classBlog(models.Model,ReadNumExpandMethod):......没有bug,真开心然后还封装了判断内容在blog\_detailinblog/views.pyinread\in_statistics/utils.py:Newutils.py#read_statistics/utils.pyfromdjango.contrib.contenttypes.modelsimportContentTypefrom.modelsimportReadNumdefread_statistics_once_read(request,obj):ct=ContentType.objects.get_for_model(obj)key="%s_%s_read"%(ct.model,obj.pk)#因为没有设置set_cookie的过期时间,所以默认是关闭浏览器时删除cookie,所以刷新无效,重新打开才会有效!ifnotrequest.COOKIES.get(key):ifReadNum.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)#count加一readnum.read_num+=1readnum.save()returnkey#blog/views.pyfromread_statistics.utilsimportread_statistics_once_read......defblog_detail(request,blog_pk):read_cookie_key=read_statistics_once_read(request,blog)......response.set_cookie(read_cookie_key,'true')...完成