基于Django contrib Comments 评论模块(详解)
老版本的Django中自带一个评论框架。但是从1.6版本后,该框架独立出去了,也就是本文的评论插件。
这个插件可给models附加评论,因此常被用于为博客文章、图片、书籍章节或其它任何东西添加评论。
一、快速入门
快速使用步骤:
安装包:pipinstalldjango-contrib-comments
在django的settings中的INSTALLED_APPS处添加'django.contrib.sites'进行app注册,并设置SITE_ID值。
在django的settings中的INSTALLED_APPS处添加'django_comments'.
运行manage.pymigrate创建评论数据表。
在项目的根urls.py文件中添加URLs:url(r'^comments/',include('django_comments.urls')),
使用comment的模板标签,将评论嵌入到你的模板中。
1.1comment模板标签
使用前请load标签:
{%loadcomments%}
1.1.1评论对象
有两种办法:
1、直接引用评论对象。假设你的模板里已经有了一个叫做entry的评论对象,那么可以使用下面的方法获得该对象的评论次数:
{%get_comment_countforentryascomment_count%}
2、使用对象的类型和id进行引用。比如,你知道一个blog的entry的id为14,那么可以这么做:
{%get_comment_countforblog.entry14ascomment_count%}
1.1.2展示评论
使用render_comment_list或者get_comment_list标签展示评论。
快速展示评论:
{%render_comment_listfor[object]%}
这会使用插件里的comments/list.html模板来生成评论的html代码。
自定义展示评论:
{%get_comment_listfor[object]as[varname]%}
实例:
{%get_comment_listforeventascomment_list%} {%forcommentincomment_list%} ... {%endfor%}
这种方式下,你可以自己控制comment的展示方式,例如添加css,js,结合bootstrap。
1.1.3为评论添加超级链接
使用get_comment_permalink标签为评论添加永久的超级链接。用法:
{%get_comment_permalinkcomment_obj[format_string]%}
默认情况下,url中的命名锚以字母“c”加评论id组成。例如:‘c82'。当然,也可以通过下面的方式自定义:
{%get_comment_permalinkcomment"#c%(id)s-by-%(user_name)s"%}
使用的是python标准格式化字符串的方式。
不管你是否自定义也好,你都必须在模板的合适位置提供一个匹配命名锚的机制。例如:
{%forcommentincomment_list%}permalinkforcomment#{{forloop.counter}} ... {%endfor%}
这块内容在使用safari浏览器的时候可能有个bug。
1.1.4评论数
获取评论数量:
{%get_comment_countfor[object]as[varname]%}
例如:
{%get_comment_countforentryascomment_count%}
Thisentryhas{{comment_count}}comments.
1.1.5评论表单
使用render_comment_form或者get_comment_form在页面上显示输入评论的表单。
快速显示表单:
{%render_comment_formfor[object]%}
使用了默认的comments/form.html模板。简单说就是傻瓜式,最丑的界面。
自定义表单:
使用get_comment_form标签获取一个form对象,然后自己写逻辑控制它的展示方式。
{%get_comment_formfor[object]as[varname]%}
展示例子(当然,这个也很丑!):
{%get_comment_formforeventasform%}
提交地址:
上面的例子通过一个comment_form_target标签为form表单指定了正确的评论内容提交地址,请务必使用该方法:
提交后的重定向地址:
如果想在用户评论后将页面重定向到另外一个地址,请在form中插入一个隐藏的input标签,并命名为next,如下所示:
为已认证用户提供不同的表单:
很多时候我们要为登录的认证用户提供一些不同于匿名用户的内容,比如姓名、邮箱、网址等等,这些可以从用户数据和信息表内获得。其实,现在大多数的网站也只允许认证用户进行评论。要做到这点,你只需要简单的展示用户信息,或修改form表单即可,例如:
{%ifuser.is_authenticated%} {%get_comment_formforobjectasform%}{%csrf_token%} {{form.comment}} {{form.honeypot}} {{form.content_type}} {{form.object_pk}} {{form.timestamp}} {{form.security_hash}} {%else%} 请先
{%endif%}登录后方可评论.
上例中的honeypot(蜜罐,一种对攻击方进行欺骗的技术),能被用户看见,因此需要利用CSS将它隐藏起来。
#id_honeypot{ display:none; }
如果你想同时接受匿名评论,只需要将上面的else从句后面的代码修改为一个标准的评论表单就可以了。
1.1.6评论表单注意事项
该插件的评论表单有一些重要的反垃圾机制,你需要特别注意:
form中包含了一些隐藏的域,例如评论对象的时间戳、信息等,还有一个用于验证信息的安全哈希。如果有不怀好意的人篡改这些数据,评论会被拒绝。如果你使用自定义的form,请确保这些字段原样的被引用。
时间戳用于确保“回复攻击”不会持续太久时间。那些在请求表单和提交表单时间差过长的用户,将被拒绝提交评论。(注:官档的意思是评论提交有时间限制要求?)
评论表单有一个honeypot域。这是一个陷阱,如果该域被填入任何数据,那么该评论会被拒绝提交。因为垃圾发送者往往自动的为表单的所有域填入一定数据,视图制造一个合法合格的提交数据单。
默认表单中上面的域都通过CSS进行了隐藏,并提供警告。如果你是自定义表单,请确保进行了同样的工作!
最后,本插件的防御机制,依赖django的csrf中间件,请确保它是开着的!否则,请使用csrf_protect装饰器对所有的使用评论表单的views进行装饰。
二、评论models
原型:classdjango_comments.models.Comment
它包含下面的字段:
content_object
评论的对象,例如一篇博客、图片、文章等等。这是一个GenericForeignKey外键。
content_type
一个指向ContentType的外键,用于保存评论对象的类型。要和上面的object区别开。
object_pk
对象的主键。一个TextField域。
site
评论提交的站点。外键。
user
指向评论的用户的外键。当匿名时,值为空。
user_name
用户名
user_email
用户邮箱
user_url
用户的网址。(很久以前的形式,现在基本都不要求填这个了。)
comment
评论的内容主体
submit_date
提交日期
ip_address
用户ip
is_public
True,则显示到页面上。
False,不显示到页面上。
is_removed
True,如果评论被移除了。用于跟踪那些被移除的评论,而不是简单的把他们直接删除。
(例如,有人言论不合适,管理员可以移除它,但是在原位置留下提示信息。)
源码:
from__future__importunicode_literals fromdjango.confimportsettings fromdjango.contrib.contenttypes.fieldsimportGenericForeignKey fromdjango.contrib.contenttypes.modelsimportContentType fromdjango.contrib.sites.modelsimportSite fromdjango.dbimportmodels fromdjango.utilsimporttimezone fromdjango.utils.encodingimportpython_2_unicode_compatible fromdjango.utils.translationimportugettext_lazyas_ try: fromdjango.urlsimportreverse exceptImportError: fromdjango.core.urlresolversimportreverse#Django<1.10 from.managersimportCommentManager COMMENT_MAX_LENGTH=getattr(settings,'COMMENT_MAX_LENGTH',3000) classBaseCommentAbstractModel(models.Model): """ Anabstractbaseclassthatanycustomcommentmodelsprobablyshould subclass. """ #Content-objectfield content_type=models.ForeignKey(ContentType, verbose_name=_('contenttype'), related_name="content_type_set_for_%(class)s", on_delete=models.CASCADE) object_pk=models.TextField(_('objectID')) content_object=GenericForeignKey(ct_field="content_type",fk_field="object_pk") #Metadataaboutthecomment site=models.ForeignKey(Site,on_delete=models.CASCADE) classMeta: abstract=True defget_content_object_url(self): """ GetaURLsuitableforredirectingtothecontentobject. """ returnreverse( "comments-url-redirect", args=(self.content_type_id,self.object_pk) ) @python_2_unicode_compatible classCommentAbstractModel(BaseCommentAbstractModel): """ Ausercommentaboutsomeobject. """ #Whopostedthiscomment?If``user``issetthenitwasanauthenticated #user;otherwiseatleastuser_nameshouldhavebeensetandthecomment #waspostedbyanon-authenticateduser. user=models.ForeignKey(settings.AUTH_USER_MODEL,verbose_name=_('user'), blank=True,null=True,related_name="%(class)s_comments", on_delete=models.SET_NULL) user_name=models.CharField(_("user'sname"),max_length=50,blank=True) #Explicit`max_length`toapplybothtoDjango1.7and1.8+. user_email=models.EmailField(_("user'semailaddress"),max_length=254, blank=True) user_url=models.URLField(_("user'sURL"),blank=True) comment=models.TextField(_('comment'),max_length=COMMENT_MAX_LENGTH) #Metadataaboutthecomment submit_date=models.DateTimeField(_('date/timesubmitted'),default=None,db_index=True) ip_address=models.GenericIPAddressField(_('IPaddress'),unpack_ipv4=True,blank=True,null=True) is_public=models.BooleanField(_('ispublic'),default=True, help_text=_('Uncheckthisboxtomakethecommenteffectively' 'disappearfromthesite.')) is_removed=models.BooleanField(_('isremoved'),default=False, help_text=_('Checkthisboxifthecommentisinappropriate.' 'A"Thiscommenthasbeenremoved"messagewill' 'bedisplayedinstead.')) #Manager objects=CommentManager() classMeta: abstract=True ordering=('submit_date',) permissions=[("can_moderate","Canmoderatecomments")] verbose_name=_('comment') verbose_name_plural=_('comments') def__str__(self): return"%s:%s..."%(self.name,self.comment[:50]) defsave(self,*args,**kwargs): ifself.submit_dateisNone: self.submit_date=timezone.now() super(CommentAbstractModel,self).save(*args,**kwargs) #后面省略
三、自定义评论框架
很明显,这个插件还不够强大,功能还不够丰富,界面还不够美观。我们必须自定义整体框架!那么怎么办呢?
假如你自己在django-contrib-commests的基础上二次开发了一个叫做my_comment_app的评论框架。请这么设置它:
INSTALLED_APPS=[ ... 'my_comment_app', ... ] COMMENTS_APP='my_comment_app'
在my_comment_app的__init__.py中定义新的模型级别的动作或行为。
简单的例子
例如有的网站希望用户在评论的时候,提供一个标题title。很显然现有的插件中的model没有这个字段,你必须自定义。怎么做?分三步:
1.创建一个自定义的comment模型,添加一个title字段;
2.创建一个自定义的commentform模型,同样地增加title字段;
3.自定义一个comment_app,定义一些新的方法,然后通知Django
如下创建包:
my_comment_app/ __init__.py models.py forms.py
在models.py文件中编写一个CommentWithTitle模型类:
fromdjango.dbimportmodels fromdjango_comments.abstractsimportCommentAbstractModel classCommentWithTitle(CommentAbstractModel): title=models.CharField(max_length=300)
然后在forms.py文件中编写新的form类,同时重写CommentForm.get_comment_create_data()方法,帮助我们增加title字段。
fromdjangoimportforms fromdjango_comments.formsimportCommentForm frommy_comment_app.modelsimportCommentWithTitle classCommentFormWithTitle(CommentForm): title=forms.CharField(max_length=300) defget_comment_create_data(self): #使用父类的数据的同时增加title字段 data=super(CommentFormWithTitle,self).get_comment_create_data() data['title']=self.cleaned_data['title'] returndata
注:在django_comments.forms中提供了一些“helper”类,帮助我们更方便地进行自定义。
最后在my_comment_app/init.py中编写方法,通知Django我们所做的改动:
defget_model(): frommy_comment_app.modelsimportCommentWithTitle returnCommentWithTitle defget_form(): frommy_comment_app.formsimportCommentFormWithTitle returnCommentFormWithTitle
注意:上面的import语句必须放在函数体内部,因为最新版本的django不允许在app的__init__.py的顶部import模块。
注意:不要循环导入模块,不要重复引入模块!
更多的自定义API
上面的例子是个通用的做法,如果还不能满足需求,那么可以使用下面的api,所有的自定义app都必须定义至少其中之一:
django_comments.get_model()
返回你要使用的自定义comment类。(请结合上面的例子进行理解。)
django_comments.get_form()
返回你要使用的自定义的commentform类。同上。
django_comments.get_form_target()
返回form在post时,提交的url地址。
django_comments.get_flag_url()
返回“flagthiscomment”视图的URL
默认情况下,它指的是django_comments.views.moderation.flag()
django_comments.get_delete_url()
返回“deletethiscomment”视图的URL
默认情况下是django_comments.views.moderation.delete()
django_comments.get_approve_url()
返回“approvethiscommentfrommoderation”视图的URL
默认情况下是django_comments.views.moderation.approve()
总结:DjangoComment评论插件原生的界面比较丑陋,但是通过自定制,可以改写出美观、适用的评论系统,比如博主个人主页的评论系统!
以上这篇基于DjangocontribComments评论模块(详解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。