基于Django contrib Comments 评论模块(详解)


Posted in Python onDecember 08, 2017

老版本的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 评论对象

有两种办法:

1、直接引用评论对象。假设你的模板里已经有了一个叫做entry的评论对象,那么可以使用下面的方法获得该对象的评论次数:

{% get_comment_count for entry as comment_count %}

2、使用对象的类型和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

用户ip

is_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没有这个字段,你必须自定义。怎么做?分三步:

1.创建一个自定义的comment模型,添加一个title字段;

2.创建一个自定义的comment form模型,同样地增加title字段;

3.自定义一个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 评论插件原生的界面比较丑陋,但是通过自定制,可以改写出美观、适用的评论系统,比如博主个人主页的评论系统!

以上这篇基于Django contrib Comments 评论模块(详解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python 网页解析HTMLParse的实例详解
Aug 10 Python
Python爬虫获取图片并下载保存至本地的实例
Jun 01 Python
Anaconda 离线安装 python 包的操作方法
Jun 11 Python
python组合无重复三位数的实例
Nov 13 Python
python各层级目录下import方法代码实例
Jan 20 Python
python判断两个序列的成员是否一样的实例代码
Mar 01 Python
使用Python对Dicom文件进行读取与写入的实现
Apr 20 Python
python3.7添加dlib模块的方法
Jul 01 Python
Django静态文件加载失败解决方案
Aug 26 Python
Python如何在bool函数中取值
Sep 21 Python
python 实现有道翻译功能
Feb 26 Python
如何利用python实现列表嵌套字典取值
Jun 10 Python
Python数据分析中Groupby用法之通过字典或Series进行分组的实例
Dec 08 #Python
python在ubuntu中的几种安装方法(小结)
Dec 08 #Python
Python编程之gui程序实现简单文件浏览器代码
Dec 08 #Python
Python中的pygal安装和绘制直方图代码分享
Dec 08 #Python
python的unittest测试类代码实例
Dec 07 #Python
Python numpy 常用函数总结
Dec 07 #Python
分享6个隐藏的python功能
Dec 07 #Python
You might like
PHP遍历数组的三种方法及效率对比分析
2015/02/12 PHP
在WordPress中使用PHP脚本来判断访客来自什么国家
2015/12/10 PHP
laravel项目利用twemproxy部署redis集群的完整步骤
2018/05/11 PHP
Laravel框架查询构造器简单示例
2019/05/08 PHP
Jquery 高亮显示文本中重要的关键字
2009/12/24 Javascript
JQuery 自定义CircleAnimation,Animate方法学习笔记
2011/07/10 Javascript
JS 控制小数位数的实现代码
2011/08/02 Javascript
jsvascript图像处理—(计算机视觉应用)图像金字塔
2013/01/15 Javascript
使用javascript实现ListBox左右全选,单选,多选,全请
2013/11/07 Javascript
js制作带有遮罩弹出层实现登录注册表单特效代码分享
2015/09/05 Javascript
快速实现JS图片懒加载(可视区域加载)示例代码
2017/01/04 Javascript
JavaScript条件判断_动力节点Java学院整理
2017/06/26 Javascript
JS实现评价的星星功能
2017/08/20 Javascript
详解node.js中的npm和webpack配置方法
2018/01/21 Javascript
Nodejs使用Mongodb存储与提供后端CRD服务详解
2018/09/04 NodeJs
基于vue.js组件实现分页效果
2018/12/29 Javascript
Python实现霍夫圆和椭圆变换代码详解
2018/01/12 Python
NumPy 如何生成多维数组的方法
2018/02/05 Python
详解Python解决抓取内容乱码问题(decode和encode解码)
2019/03/29 Python
500行Python代码打造刷脸考勤系统
2019/06/03 Python
python实现简单聊天室功能 可以私聊
2019/07/12 Python
Django url,从一个页面调到另个页面的方法
2019/08/21 Python
iPython pylab模式启动方式
2020/04/24 Python
python和js交互调用的方法
2020/06/23 Python
通过实例解析Python RPC实现原理及方法
2020/07/07 Python
HTML5实现的震撼3D焦点图动画的示例代码
2019/09/26 HTML / CSS
牦牛毛户外探险服装:Kora
2019/02/08 全球购物
党员年终民主评议的自我评价
2013/11/05 职场文书
给民警的表扬信
2014/01/08 职场文书
2015年物业管理工作总结
2015/04/23 职场文书
教师继续教育反思周记
2015/06/25 职场文书
幼儿园保育员随笔
2015/08/14 职场文书
奖学金申请书(范文)
2019/08/14 职场文书
带你彻底理解JavaScript中的原型对象
2021/04/14 Javascript
Python实战之疫苗研发情况可视化
2021/05/18 Python
python中数组和列表的简单实例
2022/03/25 Python