Django contrib Comments 评论模块详解
老版本的Django中自带一个评论框架。但是从1.6版本后,该框架独立出去了,也就是本文的评论插件。
这个插件可给models附加评论,因此常被用于为博客文章、图片、书籍章节或其它任何东西添加评论。
一、快速入门
快速使用步骤:
- 安装包: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 %}
This entry has {{ comment_count }} comments.
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_literals
from 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.10
from .managers import CommentManager
COMMENT_MAX_LENGTH = getattr(settings, 'COMMENT_MAX_LENGTH', 3000)
class BaseCommentAbstractModel(models.Model):
"""
An abstract base class that any custom comment models probably should
subclass.
"""
# Content-object field
content_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 comment
site = models.ForeignKey(Site, on_delete=models.CASCADE)
class Meta:
abstract = True
def 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 comment
submit_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.'))
# Manager
objects = CommentManager()
class Meta:
abstract = True
ordering = ('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__.py
models.py
forms.py
在models.py文件中编写一个CommentWithTitle模型类:
from django.db import models
from django_comments.abstracts import CommentAbstractModel
class 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 CommentWithTitle
class 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 CommentWithTitle
return CommentWithTitle
def get_form():
from my_comment_app.forms import CommentFormWithTitle
return 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 评论插件原生的界面比较丑陋,但是通过自定制,可以改写出美观、适用的评论系统,比如博主个人主页的评论系统!
欢迎大家访问我的个人网站《刘江的博客和教程》www.liujiangblog.com
主要分享Python 及Django教程以及相关的博客!
Django contrib Comments 评论模块详解的更多相关文章
- Django权限系统auth模块详解
转自:原文出处 auth模块是Django提供的标准权限管理系统,可以提供用户身份认证, 用户组和权限管理. auth可以和admin模块配合使用, 快速建立网站的管理系统. 在INSTALLED_A ...
- Python Fabric模块详解
Python Fabric模块详解 什么是Fabric? 简单介绍一下: Fabric是一个Python的库和命令行工具,用来提高基于SSH的应用部署和系统管理效率. 再具体点介绍一下,Fabri ...
- Python中操作mysql的pymysql模块详解
Python中操作mysql的pymysql模块详解 前言 pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎相同.但目前pymysql支持python3.x而后者不支持 ...
- python之OS模块详解
python之OS模块详解 ^_^,步入第二个模块世界----->OS 常见函数列表 os.sep:取代操作系统特定的路径分隔符 os.name:指示你正在使用的工作平台.比如对于Windows ...
- python之sys模块详解
python之sys模块详解 sys模块功能多,我们这里介绍一些比较实用的功能,相信你会喜欢的,和我一起走进python的模块吧! sys模块的常见函数列表 sys.argv: 实现从程序外部向程序传 ...
- python中threading模块详解(一)
python中threading模块详解(一) 来源 http://blog.chinaunix.net/uid-27571599-id-3484048.html threading提供了一个比thr ...
- python time 模块详解
Python中time模块详解 发表于2011年5月5日 12:58 a.m. 位于分类我爱Python 在平常的代码中,我们常常需要与时间打交道.在Python中,与时间处理有关的模块就包括: ...
- python time模块详解
python time模块详解 转自:http://blog.csdn.net/kiki113/article/details/4033017 python 的内嵌time模板翻译及说明 一.简介 ...
- 小白的Python之路 day5 time,datatime模块详解
一.模块的分类 可以分成三大类: 1.标准库 2.开源模块 3.自定义模块 二.标准库模块详解 1.time与datetime 在Python中,通常有这几种方式来表示时间:1)时间戳 2)格式化的时 ...
随机推荐
- 【UML 建模】类图介绍
1.类图是面向对象系统建模中最常用和最重要的图,是定义其它图的基础.类图主要是用来显示系统中的类.接口以及它们之间的静态结构和关系的一种静态模型. 2.类的关系有泛化(Generalization). ...
- Hadoop Streaming详解
一: Hadoop Streaming详解 1.Streaming的作用 Hadoop Streaming框架,最大的好处是,让任何语言编写的map, reduce程序能够在hadoop集群上运行:m ...
- 本地文件与服务器文件同步shell脚本。
#!/bin/sh read -t 30 -p "请输入项目名:" name echo -e "\n" echo "项目名为:$name" ...
- x86-64栈帧中的“红色区域” red zone of stack frame on x86-64
前几天看System V AMD64 ABI标准的时候发现栈帧的顶部后面有一块"red zone",在学cs:app3e/深入理解操作系统的时候并没有遇到这个,总结一下. 引用标准 ...
- Branch Prediction
Pipeline的优点 现代微处理器的pipeline中包含许多阶段,粗略地可以分成fetch.decode.execution.retirement,细分开来可以分成十多甚至二十多个阶段.在处理器处 ...
- Flex布局:实现左右两列自伸缩撑满效果的
目前测试支持的浏览器: 兼容IE10及以上.Chrom.Firefox浏览器. 假如考虑IE10以下浏览器,可以考虑其他写法. 话不多说,上代码: <div style="back ...
- 【DG】利用闪回数据库(flashback)修复Failover后的DG环境
利用闪回数据库(flashback)修复Failover后的DG环境 1.1 BLOG文档结构图 1.2 前言部分 1.2.1 导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能, ...
- Python中time和datetime模块的简单用法
python中与时间相关的一个模块是time模块,datetime模块可以看为是time模块的高级封装. time模块中经常用到的有一下几个方法: time()用来获取时间戳,表示的结果为从1970年 ...
- Python模块:paramiko
paramiko是一个用于做远程控制的模块,使用该模块可以对远程服务器进行命令或文件操作,值得一说的是,fabric和ansible内部的远程管理就是使用的paramiko来现实. 1.下载安装 Wi ...
- DIY 温控烙铁
由于工艺原因,某处要使用200W大功率烙铁(恒温烙铁虽然有那么大功率,但没有那么大的烙铁头),只能选用普通电热丝烙铁(无温控),存在温度过高现象(造成工艺不良,同时因助焊剂+高温造成烙铁头腐蚀),逐渐 ...