zoukankan      html  css  js  c++  java
  • Django contrib Comments 评论模块详解

    老版本的Django中自带一个评论框架。但是从1.6版本后,该框架独立出去了,也就是本文的评论插件。

    这个插件可给models附加评论,因此常被用于为博客文章、图片、书籍章节或其它任何东西添加评论。

    一、快速入门

    快速使用步骤:

    1. 安装包:pip install django-contrib-comments
    2. 在django的settings中的INSTALLED_APPS处添加'django.contrib.sites'进行app注册,并设置SITE_ID值。
    3. 在django的settings中的INSTALLED_APPS处添加'django_comments'.
    4. 运行manage.py migrate创建评论数据表。
    5. 在项目的根urls.py文件中添加URLs:url(r'^comments/', include('django_comments.urls')),
    6. 使用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 评论插件原生的界面比较丑陋,但是通过自定制,可以改写出美观、适用的评论系统,比如博主个人主页的评论系统!


    欢迎大家访问我的个人网站《刘江的博客和教程》www.liujiangblog.com

    主要分享Python 及Django教程以及相关的博客!

  • 相关阅读:
    软件架构学习小结
    Azure编程笔记(1):序列化复杂类型的TableEntity字段
    目标检測的图像特征提取之(一)HOG特征
    Gmail POP3设置
    MP3的频率、比特率、码率与音质的关系
    Oracle Dataguard 介绍
    【Github教程】史上最全github用法:github入门到精通
    Xcode6在10.9.4上面crash解决
    OpenCV 编程简单介绍(矩阵/图像/视频的基本读写操作)
    调用ShellExecute所须要头文件
  • 原文地址:https://www.cnblogs.com/feixuelove1009/p/8000556.html
Copyright © 2011-2022 走看看