基于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创建线程示例
May 06 Python
Python中threading模块join函数用法实例分析
Jun 04 Python
用matplotlib画等高线图详解
Dec 14 Python
基于pandas数据样本行列选取的方法
Apr 20 Python
python获取中文字符串长度的方法
Nov 14 Python
使用PyQtGraph绘制精美的股票行情K线图的示例代码
Mar 14 Python
Python多线程threading模块用法实例分析
May 22 Python
Django 静态文件配置过程详解
Jul 23 Python
对Python 中矩阵或者数组相减的法则详解
Aug 26 Python
使用python制作一个解压缩软件
Nov 13 Python
Django 实现外键去除自动添加的后缀‘_id’
Nov 15 Python
通用的Django注册功能模块实现方法
Feb 05 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加速 eAccelerator配置和使用指南
2009/06/05 PHP
一道关于php变量引用的面试题
2010/08/08 PHP
PHP父类调用子类方法的代码例子
2014/04/09 PHP
简单介绍PHP非阻塞模式
2016/03/03 PHP
JavaScript 的方法重载效果
2009/08/07 Javascript
jQuery getJSON 处理json数据的代码
2010/07/26 Javascript
5个javascript的数字格式化函数分享
2011/12/07 Javascript
关于js遍历表格的实例
2013/07/10 Javascript
浅析JavaScript中的delete运算符
2013/11/30 Javascript
javascript中拼接HTML字符串的最快、最好的方法
2014/06/07 Javascript
推荐一个自己用的封装好的javascript插件
2015/01/29 Javascript
基于jquery实现导航菜单高亮显示(两种方法)
2015/08/23 Javascript
jQuery插件EasyUI实现Layout框架页面中弹出窗体到最顶层效果(穿越iframe)
2016/08/05 Javascript
微信小程序 两种滑动方式(横向滑动,竖向滑动)详细及实例代码
2017/01/13 Javascript
jQuery导航条固定定位效果实例代码
2017/05/26 jQuery
Vue实现路由跳转和嵌套
2017/06/20 Javascript
swiper动态改变滑动内容的实现方法
2018/01/17 Javascript
Vue 开发音乐播放器之歌手页右侧快速入口功能
2018/08/08 Javascript
angular5 子组件监听父组件传入值的变化方法
2018/09/30 Javascript
总结4个方面优化Vue项目
2019/02/11 Javascript
[05:43]VG.R战队教练Mikasa专访:为目标从未停止战斗
2016/08/02 DOTA
[01:06:54]DOTA2-DPC中国联赛 正赛 SAG vs DLG BO3 第二场 2月28日
2021/03/11 DOTA
Python的numpy库中将矩阵转换为列表等函数的方法
2018/04/04 Python
详解Django admin高级用法
2019/11/06 Python
什么是Python中的匿名函数
2020/06/02 Python
如何解决安装python3.6.1失败
2020/07/01 Python
Python通过fnmatch模块实现文件名匹配
2020/09/30 Python
Django如何使用asyncio协程和ThreadPoolExecutor多线程
2020/10/12 Python
浅谈HTML5新增及移除的元素
2016/06/27 HTML / CSS
socket.io 和canvas 实现的共享画板功能
2019/05/22 HTML / CSS
六月份红领巾广播稿
2014/02/03 职场文书
教师节表彰会主持词
2015/07/06 职场文书
亲情作文之母爱
2019/09/25 职场文书
励志语录:只有自己足够强大,才能不被别人践踏
2020/01/09 职场文书
OpenCV绘制圆端矩形的示例代码
2021/08/30 Python
关于python爬虫应用urllib库作用分析
2021/09/04 Python