在一篇文章下会有发表评论和展示评论

评论可以做一个单独的模块,不仅仅用于博文评论,还可以用作留言等等,网站一切需要评论的地方

新建app

新建一个comment APP

python manage.py startapp comment

评论涉及的基本信息有:

评论者, 评论时间, 评论内容

编写模型models

from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User


class Comment(models.Model):
    content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING)
    object_id = PositiveInteger()
    content_object = GenericForeignKey('content_type', 'object_id')
    
    text = models.TextField()
    comment_time = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(User, on_delete=models.DO_NOTHING)


    class Meta:
        ordering = ['-comment_time']

评论和统计模块一样,用处有很多,所以我们使用ContentType

这三个是固定搭配

content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING)
object_id = PositiveInteger()
content_object = GenericForeignKey('content_type', 'object_id')

下面三个,分别就是内容,时间和评论者

用户这一块,django自动创建的,有另外一个用户表,所以用外键关联起来

text = models.TextField()
comment_time = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(User, on_delete=models.DO_NOTHING)

默认取出的数据是降序排列的

ordering = ['-comment_time']

settings注册评论模块

简易登录

完成评论之前,先来做用户登录

用户登录的大致流程:

用户在前端表单输入账号密码并点击登录->服务器端接受请求->验证账号密码是否正确

按着顺序来完成吧,第一用户在前端请求登录,那么就需要一个请求登录的地址

来到路由设置,urlpatterns新增

path('login/', login, name='login'),

login视图函数还没写,先暂时放着

请求现在可以收到了,那么就需要根据这个请求的参数,进行处理

编写视图函数login

from django.shortcuts import render, redirect
from django.contrib import auth
from django.urls import reverse


def login(request):
    username = request.POST.get('username', '')
    password = request.POST.get('password', '')
    # 验证用户的账号密码是否正确
    user = auth.authenticate(request, username=username, password=password)
    # referer = request.META.get('HTTP_REFERER', '/')
    # 接收用户的上一个网页的地址
    referer = request.META.get('HTTP_REFERER', reverse('home'))
    if user:
        auth.login(request, user)
        # 登录成功跳转到某个页面
        return redirect(referer)
    else:
        # 登录失败,返回一个错误页面
        return render(request, 'error.html', {'message': '用户名或密码不正确'})

解释:

auth.authenticate用于验证账号密码是否正确

使用authenticate()验证的一组凭据。它需要的凭据作为关键字参数,username并 password默认情况下,检查他们对每一个 认证后端,并返回一个 User凭据是否有效的后端对象。如果凭证对任何后端无效或后端引发异常PermissionDenied,则返回None

request.META.get('HTTP_REFERER', reverse('home')) 当用户在一篇文章页登录成功,肯定还是跳转回这篇文章,继续观看。所以这个的作用,就是取到用户上一个访问的网页链接是什么

如果没有,就返回到主页

这里有一个reverse方法,翻译过来就是相反的,意思就是通过路由规则的别名反向获取其对应的url链接

auth.login(request, user)

如果您具有经过身份验证的用户,则希望附加到当前会话-这是通过一个login()函数完成的。

看下实际过程中,做了什么?

这是登录前的cookie

UTOOLS1588119352027.png

这是登录后的cookie

UTOOLS1588119403877.png

给浏览器设置了sessionid 这是一个加密的字符串,拥有此字段,再次请求其他页面时,服务器端就能判断这个用户的登录状态是什么

redirect还有一个重定向,就是指定跳转到哪个页面

提交评论

用户登录完成后就可以开始评论了,评论需要表单

提交表单,在表单中必须加上{% csrf_token %}

这个是新增在blog详情页模板中的,也就是文章页下方

user虽然我们没有定义,但是django在reques中帮我们定义好了的,直接拿出来用即可

通过{% if user.is_authenticated %}可以判断当前用户的登录状态

<div class="comment-submit">
    <h3>提交评论</h3>
    {% if user.is_authenticated %}
    <form action="{% url 'update_comment' %}" method="post">
        {% csrf_token %}
        <div class="form-group">
            <label for="comment_text">{{ user.username }},欢迎评论~</label>
            <textarea class="form-control" cols="30" id="comment_text" name="comment_text" rows="10"></textarea>
        </div>
        <input name="object_id" type="hidden" value="{{ blog.pk }}">
        <input name="content_type" type="hidden" value="blog">
        <input class="btn btn-primary pull-right" type="submit" value="评论">
    </form>
    {% else %}
    未登录
    <form action="{% url 'login' %}" method="post">
        {% csrf_token %}
        <label>
            用户名:<input type="text" name="username">
        </label>
        <label>
            密码:<input type="password" name="password">
        </label>
        <label for="login">
            <input type="submit" value="登录">
        </label>
    </form>
    {% endif %}
</div>

提交表单,我们需要的信息

文章id,评论类型,评论内容,上述例子中表单对应的名字

object_id, content_type, comment_text

这个很关键,因为后端取这些数据,就靠这些我们自定义的名字

填写了表单需要请求评论链接

<form action="{% url 'update_comment' %}" method="post">

所以又要去设置路由, 别名这是为update_comment

comment模块内设置详细路由规则

from django.urls import path
from .views import update_comment


urlpatterns = [
    path('update_comment', update_comment, name="update_comment")
]

全局路由urls中导入

path('comment/', include('comment.urls')),

update_comment编写评论处理的视图函数

这里有些繁琐,主要就是判断每条数据的有效性

def update_comment(request):
    """提交评论"""
    user = request.user
    referer = request.META.get('HTTP_REFERER', reverse('home'))

    # 判断用户是否有效
    if not user.is_authenticated:
        return render(request, 'error.html', {'message': '未登录', 'redirect_to': referer})

    comment_text = request.POST.get('comment_text', '').strip()
    # 判断内容是否为空
    if comment_text == '':
        return render(request, 'error.html', {'message': '评论内容为空', 'redirect_to': referer})

    try:
        content_type = request.POST.get('content_type', '')
        object_id = int(request.POST.get('object_id', ''))
        comment = Comment()
        # 接收到的content_type 是一个字符串,通过下方方法是,获取其模型类
        model_class = ContentType.objects.get(model=content_type).model_class()
    except:
        return render(request, 'error.html', {'message': '评论对象不存在', 'redirect_to': referer})
    
    # 提交评论
    comment.user = user
    comment.text = comment_text
    comment.content_object = model_class.objects.get(pk=object_id)
    comment.save()

    return redirect(referer)

这里值得注意是,从这个请求过来的参数值,均是字符串类型

content_type这个类型也是字符串,所以需要通过这个字符串去得到真正的内容类型

可以使用ContentType.objects.get(model=content_type).model_class()

ContentType之前说过,可以看做是一个数据表,ContentType.objects.get(model=content_type)就是去数据表中查询此类,再通过model_class()方法得到此类内容的模型类

UTOOLS1588123018601.png

展示评论

既然展示在文章页,所以就在文章页的视图函数下编写吧

在文章页的视图函数中,我们可以知道当前文章的id

用这个id可以去找到所对应的所有评论数据

视图

# ...
blog = get_object_or_404(Blog, pk=pk)
content_type = ContentType.objects.get_for_models(blog)
comment_list = Comment.objectc.filter(content_type=content_type, object_id=pk)
context['comment_list'] = comment_list
# ...

模板(粗略展示)

<div class="comment-show">
    <h3>评论展示</h3>
    {% for comment in comment_list %}
    {{ comment.user.username }}
    {{ comment.comment_time|date:"Y-m-d H:n:s" }}
    {{ comment.text }}
    {% endfor %}
</div>
Last modification:April 29th, 2020 at 09:21 am