Django contrib Comments 评论模块详解
一、快速入门
快速使用步骤:
- 安装包:pip install django-contrib-comments
- 在django的settings中的INSTALLED_APPS处添加'django.contrib.sites'进行app注册,并设置SITE_ID值。
- 在django的settings中的INSTALLED_APPS处添加'django_comments'.
- 运行manage.py migrate创建评论数据表。
- 在项目的根urls.py文件中添加URLs:url(r'^comments/', include('django_comments.urls')),
- 使用comment的模板标签,将评论嵌入到你的模板中。
1.1 comment模板标签
使用前请load标签:
{% load comments %}
1.1.1 评论对象
有两种办法:
- 直接引用评论对象。假设你的模板里已经有了一个叫做entry的评论对象,那么可以使用下面的方法获得该对象的评论次数:
{% get_comment_count for entry as comment_count %}
- 使用对象的类型和id进行引用。比如,你知道一个blog的entry的id为14,那么可以这么做:
{% get_comment_count for blog.entry 14 as comment_count %}
1.1.2 展示评论
使用render_comment_list
或者get_comment_list
标签展示评论。
快速展示评论:
{% render_comment_list for [object] %}
这会使用插件里的comments/list.html模板来生成评论的html代码。
自定义展示评论:
{% get_comment_list for [object] as [varname] %}
实例:
{% get_comment_list for event as comment_list %}
{% for comment in comment_list %}
...
{% endfor %}
这种方式下,你可以自己控制comment的展示方式,例如添加css,js,结合bootstrap。
1.1.3 为评论添加超级链接
使用get_comment_permalink标签为评论添加永久的超级链接。
用法:
{% get_comment_permalink comment_obj [format_string] %}
默认情况下,url中的命名锚以字母“c”加评论id组成。例如: ‘c82’。当然,也可以通过下面的方式自定义:
{% get_comment_permalink comment "#c%(id)s-by-%(user_name)s"%}
使用的是python标准格式化字符串的方式。
不管你是否自定义也好,你都必须在模板的合适位置提供一个匹配命名锚的机制。例如:
{% for comment in comment_list %}<a name="c{{ comment.id }}"></a><a href="{% get_comment_permalink comment %}">permalink for comment #{{ forloop.counter }}</a>...
{% endfor %}
这块内容在使用safari浏览器的时候可能有个bug。
1.1.4 评论数
获取评论数量:
{% get_comment_count for [object] as [varname] %}
例如:
{% get_comment_count for entry as comment_count %}
<input type="submit" name="preview" value="提交">
1.1.5 评论表单
使用render_comment_form或者get_comment_form在页面上显示输入评论的表单。
快速显示表单:
{% render_comment_form for [object] %}
使用了默认的comments/form.html模板。简单说就是傻瓜式,最丑的界面。
自定义表单:
使用get_comment_form标签获取一个form对象,然后自己写逻辑控制它的展示方式。
{% get_comment_form for [object] as [varname] %}
展示例子(当然,这个也很丑!):
{% get_comment_form for event as form %}
<table><form action="{% comment_form_target %}" method="post">{% csrf_token %}{{ form }}<tr><td colspan="2"><input type="submit" name="submit" value="Post"><input type="submit" name="preview" value="Preview"></td></tr></form>
</table>
提交地址:
上面的例子通过一个comment_form_target标签为form表单指定了正确的评论内容提交地址,请务必使用该方法:
<form action="{% comment_form_target %}" method="post">
提交后的重定向地址:
如果想在用户评论后将页面重定向到另外一个地址,请在form中插入一个隐藏的input标签,并命名为next,如下所示:
<input type="hidden" name="next" value="{% url 'my_comment_was_posted' %}" />
为已认证用户提供不同的表单:
很多时候我们要为登录的认证用户提供一些不同于匿名用户的内容,比如姓名、邮箱、网址等等,这些可以从用户数据和信息表内获得。其实,现在大多数的网站也只允许认证用户进行评论。要做到这点,你只需要简单的展示用户信息,或修改form表单即可,例如:
{% if user.is_authenticated %}{% get_comment_form for object as form %}<form action="{% comment_form_target %}" method="POST">{% csrf_token %}{{ form.comment }}{{ form.honeypot }}{{ form.content_type }}{{ form.object_pk }}{{ form.timestamp }}{{ form.security_hash }}<input type="hidden" name="next" value="{% url 'object_detail_view' object.id %}" /><input type="submit" value="提交评论" id="id_submit" /></form>
{% else %}<p>请先<a href="{% url 'auth_login' %}">登录</a>后方可评论.</p>
{% endif %}
上例中的honeypot(蜜罐,一种对攻击方进行欺骗的技术),能被用户看见,因此需要利用CSS将它隐藏起来。
#id_honeypot {display: none;
}
如果你想同时接受匿名评论,只需要将上面的else从句后面的代码修改为一个标准的评论表单就可以了。
1.1.6 评论表单注意事项
该插件的评论表单有一些重要的反垃圾机制,你需要特别注意:
- form中包含了一些隐藏的域,例如评论对象的时间戳、信息等,还有一个用于验证信息的安全哈希。如果有不怀好意的人篡改这些数据,评论会被拒绝。如果你使用自定义的form,请确保这些字段原样的被引用。
- 时间戳用于确保“回复攻击”不会持续太久时间。那些在请求表单和提交表单时间差过长的用户,将被拒绝提交评论。(注:官档的意思是评论提交有时间限制要求?)
- 评论表单有一个honeypot域。这是一个陷阱,如果该域被填入任何数据,那么该评论会被拒绝提交。因为垃圾发送者往往自动的为表单的所有域填入一定数据,视图制造一个合法合格的提交数据单。
默认表单中上面的域都通过CSS进行了隐藏,并提供警告。如果你是自定义表单,请确保进行了同样的工作!
最后,本插件的防御机制,依赖django的csrf中间件,请确保它是开着的!否则,请使用csrf_protect装饰器对所有的使用评论表单的views进行装饰。
二、评论models
原型:class django_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
用户ipis_public
True,则显示到页面上。
False,不显示到页面上。is_removed
True,如果评论被移除了。用于跟踪那些被移除的评论,而不是简单的把他们直接删除。
(例如,有人言论不合适,管理员可以移除它,但是在原位置留下提示信息。)
源码:
from __future__ import unicode_literalsfrom django.conf import settings
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
try:from django.urls import reverse
except ImportError:from django.core.urlresolvers import reverse # Django < 1.10from .managers import CommentManagerCOMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000)class BaseCommentAbstractModel(models.Model):"""An abstract base class that any custom comment models probably shouldsubclass."""# Content-object fieldcontent_type = models.ForeignKey(ContentType,verbose_name=_('content type'),related_name="content_type_set_for_%(class)s",on_delete=models.CASCADE)object_pk = models.TextField(_('object ID'))content_object = GenericForeignKey(ct_field="content_type", fk_field="object_pk")# Metadata about the commentsite = models.ForeignKey(Site, on_delete=models.CASCADE)class Meta:abstract = Truedef get_content_object_url(self):"""Get a URL suitable for redirecting to the content object."""return reverse("comments-url-redirect",args=(self.content_type_id, self.object_pk))@python_2_unicode_compatible
class CommentAbstractModel(BaseCommentAbstractModel):"""A user comment about some object."""# Who posted this comment? If ``user`` is set then it was an authenticated# user; otherwise at least user_name should have been set and the comment# was posted by a non-authenticated user.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's name"), max_length=50, blank=True)# Explicit `max_length` to apply both to Django 1.7 and 1.8+.user_email = models.EmailField(_("user's email address"), max_length=254,blank=True)user_url = models.URLField(_("user's URL"), blank=True)comment = models.TextField(_('comment'), max_length=COMMENT_MAX_LENGTH)# Metadata about the commentsubmit_date = models.DateTimeField(_('date/time submitted'), default=None, db_index=True)ip_address = models.GenericIPAddressField(_('IP address'), unpack_ipv4=True, blank=True, null=True)is_public = models.BooleanField(_('is public'), default=True,help_text=_('Uncheck this box to make the comment effectively ''disappear from the site.'))is_removed = models.BooleanField(_('is removed'), default=False,help_text=_('Check this box if the comment is inappropriate. ''A "This comment has been removed" message will ''be displayed instead.'))# Managerobjects = CommentManager()class Meta:abstract = Trueordering = ('submit_date',)permissions = [("can_moderate", "Can moderate comments")]verbose_name = _('comment')verbose_name_plural = _('comments')def __str__(self):return "%s: %s..." % (self.name, self.comment[:50])def save(self, *args, **kwargs):if self.submit_date is None: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没有这个字段,你必须自定义。怎么做?分三步:
- 创建一个自定义的comment模型,添加一个title字段;
- 创建一个自定义的comment form模型,同样地增加title字段;
- 自定义一个comment_app,定义一些新的方法,然后通知Django
如下创建包:
my_comment_app/__init__.pymodels.pyforms.py
在models.py文件中编写一个CommentWithTitle模型类:
from django.db import models
from django_comments.abstracts import CommentAbstractModelclass CommentWithTitle(CommentAbstractModel):title = models.CharField(max_length=300)
然后在forms.py文件中编写新的form类,同时重写CommentForm.get_comment_create_data()方法,帮助我们增加title字段。
from django import forms
from django_comments.forms import CommentForm
from my_comment_app.models import CommentWithTitleclass CommentFormWithTitle(CommentForm):title = forms.CharField(max_length=300)def get_comment_create_data(self):# 使用父类的数据的同时增加title字段data = super(CommentFormWithTitle, self).get_comment_create_data()data['title'] = self.cleaned_data['title']return data
注:在django_comments.forms中提供了一些“helper”类,帮助我们更方便地进行自定义。
最后在my_comment_app/init.py中编写方法,通知Django我们所做的改动:
def get_model():from my_comment_app.models import CommentWithTitlereturn CommentWithTitledef get_form():from my_comment_app.forms import CommentFormWithTitlereturn CommentFormWithTitle
注意:上面的import语句必须放在函数体内部,因为最新版本的django不允许在app的__init__.py的顶部import模块。
注意:不要循环导入模块,不要重复引入模块!
更多的自定义API
上面的例子是个通用的做法,如果还不能满足需求,那么可以使用下面的api,所有的自定义app都必须定义至少其中之一:
- django_comments.get_model()
返回你要使用的自定义comment类。(请结合上面的例子进行理解。) - django_comments.get_form()
返回你要使用的自定义的comment form类。同上。 - django_comments.get_form_target()
返回form在post时,提交的url地址。 - django_comments.get_flag_url()
返回“flag this comment”视图的URL
默认情况下,它指的是django_comments.views.moderation.flag() - django_comments.get_delete_url()
返回“delete this comment” 视图的URL
默认情况下是django_comments.views.moderation.delete() - django_comments.get_approve_url()
返回“approve this comment from moderation” 视图的URL
默认情况下是django_comments.views.moderation.approve()
总结: Django Comment 评论插件原生的界面比较丑陋,但是通过自定制,可以改写出美观、适用的评论系统
修改前端页面显示评论列表和评论提交表单。在使用标签之前导入加载:
- {# 导入评论库模块的模版标签 #}
- {% load comments %}
评论列表
评论列表可以通过django_comments的get_comment_list模版标签获取,如下代码:
- <div class="panel panel-default">
- <div class="panel-heading">
- <h4>评论列表</h4>
- </div>
- <div class="panel-body">
- {% get_comment_list for blog as comments %}
- {% for comment in comments %}
- <div class="blog_comment" name="F{{comment.id}}">
- <p class="comment_title">
- #{{ comment.submit_date|date:"Y-m-d H:i"}} @ {{ comment.user_name }}:
- </p>
- <p class="comment_content">{{ comment.comment }}</p>
- </div>
- {% empty %}
- <span>暂无评论</span>
- {% endfor %}
- </div>
- </div>
get_comment_list模版标签的用法是for一个模版对象,as是重命名。变量得到的评论加载即可。
提交评论
而评论提交表单,最主要的是提交的url和表单字段。同样也可以通过django_comments的模版标签处理,如下代码:
- <h4>新的评论</h4>
- {% get_comment_form for blog as blog_form %}
- <form id="comment_form" class="form-horizontal" action="{% comment_form_target %}" method="post">
- {% csrf_token %}
- {# 必须的字段 #}
- {{ blog_form.object_pk }}
- {{ blog_form.content_type }}
- {{ blog_form.timestamp }}
- {{ blog_form.site }}
- {{ blog_form.submit_date }}
- {{ blog_form.security_hash }}
- {# 用户名字段,这个后面会修改为登录用户评论,无需填这个 #}
- <div class="control-group">
- <label class="control-label" for="id_name">名称: </label>
- <div class="controls">
- <input type="text"
- id="id_name" class="input-xlarge" name="name"
- placeholder="请输入您的用户名"
- value="{{ user.username }}" />
- </div>
- </div>
- {# 邮箱地址字段 #}
- <div class="control-group">
- <label class="control-label" for="id_email">邮箱: </label>
- <div class="controls">
- <input type="email"
- id="id_email" class="input-xlarge" name="email"
- placeholder="请输入您的邮箱地址"
- value="{{ user.email }}" />
- </div>
- </div>
- {# 评论内容 #}
- <a name="newcomment" id="newcomment"></a>
- <div class="control-group">
- <label class="control-label" for="id_comment">评论: </label>
- <div class="controls">
- <textarea rows="6"
- id="id_comment" class="input-xlarge comment" name="comment"
- placeholder="请输入评论内容">
- </textarea>
- </div>
- </div>
- {# 防垃圾评论 #}
- <p style="display:none;">
- <label for="id_honeypot">如果你在该字段中输入任何内容,你的评论就会被视为垃圾评论。</label>
- <input type="text" name="honeypot" id="id_honeypot">
- </p>
- {# 表单按钮 #}
- <div class="controls">
- <div class="form-actions">
- <input class="btn btn-info" id="submit_btn" type="submit" name="submit" value="提交"/>
- <input type="hidden" name="next" value="{%url 'detailblog' blog.id%}"/>
- </div>
- </div>
- </form>
其中我使用了一些Bootstrap的样式,可忽略。虽然现在可以提交评论了,但提交评论之后会跳转到一个很简陋的页面。
接下来用ajax写评论提交事件避免避免跳转到独立的评论页面,修正时间戳等Bug。
Django的评论库如果填写不完整,或者提交出错,就会跑到自带的页面。
关键是自带的评论页面超级不好看。所以得想方法避开。
为Comments添加Ajax功能,免得提交出错跳到自带的评论页面。
具体可参考django 简易博客开发 4 comments库使用及ajax支持提交前,先在本地验证是否填写。
- {% block extra_footer %}
- {#设置提交评论#}
- <script type="text/javascript">
- $(document).ready(function() {
- $('#comment_form').submit(function() {
- if ($("#id_honeypot").val().length!=0) {
- alert("Stop!垃圾评论");
- return false;
- };
- if ($("#id_name").val().length==0) {
- alert("Error:请输入您的用户名");
- $("#id_name").focus();
- return false;
- };
- if ($("#id_email").val().length==0) {
- alert("Error:请输入您的邮箱地址");
- $("#id_email").focus();
- return false;
- };
- var email=$("#id_email").val();
- if(!email.match(/^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/)){
- alert("Error:邮箱不正确!请重新输入");
- $("#id_email").focus();
- return false;
- }
- if ($("#id_comment").val().length==0){
- alert("Error:请输入您的评论");
- $("#id_comment").focus();
- return false;
- };
- $("#id_timestamp").value=event.timeStamp;
- $.ajax({
- type: "POST",
- data: $('#comment_form').serialize(),
- url: "{% comment_form_target %}",
- cache: false,
- dataType: "html",
- success: function(html, textStatus) {
- window.location.reload();
- },
- error: function (XMLHttpRequest, textStatus, errorThrown) {
- alert("评论出错," + errorThrown);
- }
- });
- return false;
- });
- });
- </script>
- {% endblock %}
就这样实现了ajax提交。只有后台运行出错,才会返回error错误。
Django contrib Comments 评论模块详解相关推荐
- Django权限系统auth模块详解
转自:原文出处 auth模块是Django提供的标准权限管理系统,可以提供用户身份认证, 用户组和权限管理. auth可以和admin模块配合使用, 快速建立网站的管理系统. 在INSTALLED_A ...
- 43.Django权限系统auth模块详解
昨天我们为了登录admin,通过命令创建了超级用户,你是不是有个疑问--这创建的超级用户的信息是存放在哪里了呢? 这就想到了我们映射数据库时,Django自动创建的一些表(这也是之前进行数据库迁移 ...
- python哪个关键字可以导入模块_关于python导入模块import与常见的模块详解
0.什么是python模块?干什么的用的? Java中如果使用abs()函数,则需要需要导入Math包,同样python也是封装的,因为python提供的函数太多,所以根据函数的功能将其封装在不同的m ...
- [系统安全] 四十五.APT系列(10)Metasploit后渗透技术信息收集、权限提权和功能模块详解
您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...
- python中导入模块是用哪个关键字_关于python导入模块import与常见的模块详解
0.什么是python模块?干什么的用的? Java中如果使用abs()函数,则需要需要导入Math包,同样python也是封装的,因为python提供的函数太多,所以根据函数的功能将其封装在不同的m ...
- 【ES6】Module模块详解
[ES6]Module模块详解 一.Module的由来 二.严格模式 三.export命令 四.import命令 查看更多ES6教学文章: 参考文献 引言:由于两个JS文件之间相互使用必须通过一个ht ...
- python常用内置模块-Python常用内置模块之xml模块(详解)
xml即可扩展标记语言,它可以用来标记数据.定义数据类型,是一种允许用户对自己的标记语言进行定义的源语言.从结构上,很像HTML超文本标记语言.但他们被设计的目的是不同的,超文本标记语言被设计用来显示 ...
- python之sys模块详解_(转)python之os,sys模块详解
python之sys模块详解 原文:http://www.cnblogs.com/cherishry/p/5725184.html sys模块功能多,我们这里介绍一些比较实用的功能,相信你会喜欢的,和 ...
- Ansible基本使用及常用模块详解
一.ansible基本使用 定义主机组 定义被管理节点列表的文件/etc/ansible/hosts,在定义被管理节点的时候,可以单独基于主机做定义,也可以将多个主机定义成一个主机组. 在上篇博文安装 ...
最新文章
- 一次非常有意思的 SQL 优化经历:从 30248.271s 到 0.001s
- vs2010给c语言文件添加头注释
- Linux关于终端的基本概念汇总(tty/pty)(转)
- java.net.SocketTimeoutException: Read timed out 异常排查
- 《精通 ASP.NET MVC 5》----1.8 本书所需的软件
- PLSQL_基础系列11_递归和层次查询CONNECT BY(案例)
- Javascript权威指南学习笔记一:数据类型
- SwipeRefreshLayout官方推荐下拉刷新
- 录制wav格式的音频
- 英特尔:把基带卖给苹果 完全是高通逼的
- MyBatis深入(2)-项目结构
- 最大似然估计(MLE)的一些公式与定理(python实践)
- Linux中Redis操作命令
- 浅析帧、帧数、帧率、FPS区别
- ES集群不通,日志报[node-3] not enough master nodes discovered during pinging (found [[Candidate{node={node-3
- android 文件管理器打开方式,android怎么用浏览器打开浏览器文件?
- React组件三大属性props state refs以及组件的生命周期
- springboot微信点餐系统项目设计
- 树链剖分——杨子曰算法
- 做一个有流量的标题党
热门文章
- mysql 签到 存储,MySQL和Redis实现用户签到,你喜欢怎么实现?
- prim算法适用条件_Prim算法和Kruskal算法介绍
- 算法笔记【二】DFS
- MySQL(5.7版本)安装 + windows远程连接 Linux MySQL
- maven3实战之仓库(远程仓库的配置)
- 药店行政处罚新标准来了!15大行为千万别碰!
- 数仓实战 - 滴滴出行
- vue中页面数据改变组件不重新渲染
- GISer从零开始学习ArcGIS API for JavaScriptArcGIS Online教程(二)第一个地图应用
- PTA 7-1 老师生日