Diango博客--10.交流的桥梁“评论功能”

文章目录

    • 0.思路引导
    • 1.创建"评论"应用
    • 2.设计"评论"的数据库模型
    • 3.注册"评论"模型到 admin
    • 4.设计“评论”表单
    • 5.展示评论表单
    • 6.“评论”视图函数
    • 7.绑定 URL
    • 8.向读者发送是否“评论”成功的状态
    • 9.详情页底部显示“评论”内容

0.思路引导

本文将创建一个新的应用,即在博客详情页的底部,增加评论功能。

效果显示如下所示,具体的操作将从头到尾进行记录。

在这里插入图片描述

1.创建"评论"应用

1)进入到项目根目录,然后输入如下命令创建一个新的应用:

pipenv run python manage.py startapp comments

2)在 settings.py 里注册这个应用,django 才知道这是一个应用
文件位置:blogproject/settings.py

...
INSTALLED_APPS = [...'blog.apps.BlogConfig',  # 注册 blog 应用'comments.apps.CommentsConfig',  # 注册 comments 应用
]v
...

3)注意这里注册的是 CommentsConfig 类,同时进行配置,让 comment 应用在 django 的 admin 后台显示中文名字。

文件位置:comments/app.py

from django.apps import AppConfigclass CommentsConfig(AppConfig):name = 'comments'verbose_name = '评论'

2.设计"评论"的数据库模型

1)编写数据库模型文件
文件位置:comments/models.py


from django.db import models
from django.utils import timezoneclass Comment(models.Model):name = models.CharField('名字', max_length=50)email = models.EmailField('邮箱')url = models.URLField('网址', blank=True)text = models.TextField('内容')created_time = models.DateTimeField('创建时间', default=timezone.now)post = models.ForeignKey('blog.Post', verbose_name='文章', on_delete=models.CASCADE)class Meta:verbose_name = '评论'verbose_name_plural = verbose_namedef __str__(self):return '{}: {}'.format(self.name, self.text[:20])

2)迁移数据库
在项目根目录下分别运行下面两条命令:

pipenv run python manage.py makemigrations
pipenv run python manage.py migrate

3.注册"评论"模型到 admin

既然已经创建了模型,我们就可以将它注册到 django admin 后台,方便管理员用户对评论进行管理

文件位置:comments/admin.py

from django.contrib import admin
from .models import Commentclass CommentAdmin(admin.ModelAdmin):list_display = ['name', 'email', 'url', 'post', 'created_time']fields = ['name', 'email', 'url', 'text', 'post']admin.site.register(Comment, CommentAdmin)

实际效果展示如下:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

4.设计“评论”表单

思维逻辑:

1)表单是用来收集并向服务器提交用户输入的数据的,是用户在我们博客网站上发表评论的过程。

2)当用户想要发表评论时,他找到我们给他展示的一个评论表单(在文章详情页的底部就有一个评论表单),然后根据表单的要求填写相应的数据。

3)之后用户点击评论按钮,这些数据就会发送给某个 URL。我们知道每一个 URL 对应着一个 django 的视图函数,于是 django 调用这个视图函数,我们在视图函数中,对用户通过表单提交上来的数据进行处理,比如验证数据的合法性并且保存数据到数据库中,那么用户的评论就被 django 处理了。

4)如果通过表单提交的数据存在错误,那么我们把错误信息返回给用户,并在前端重新渲染表单,要求用户根据错误信息修正表单中不符合格式的数据,再重新提交。

文件位置:comments/forms.py

from django import forms
from .models import Commentclass CommentForm(forms.ModelForm):class Meta:model = Commentfields = ['name', 'email', 'url', 'text']

要使用 django 的表单功能,我们首先导入 forms 模块。django 的表单类必须继承自 forms.Form 类或者 forms.ModelForm 类。

如果表单对应有一个数据库模型(例如这里的评论表单对应着评论模型),那么使用 ModelForm 类会简单很多,这是 django 为我们提供的方便。

之后我们在表单的内部类 Meta 里指定一些和表单相关的东西。model = Comment 表明这个表单对应的数据库模型是 Comment 类。fields = [‘name’, ‘email’, ‘url’, ‘text’] 指定了表单需要显示的字段,这里我们指定了 name、email、url、text 需要显示。
在这里插入图片描述

5.展示评论表单

表单类已经定义完毕,现在的任务是在文章的详情页下方将这个表单展现给用户,用户可以通过这个表单填写评论数据,从而发表评论。

那么怎么展现一个表单呢?django 会根据表单类的定义自动生成表单的 HTML 代码,我们要做的就是实例化这个表单类,然后将表单的实例传给模板,让 django 的模板引擎来渲染这个表单。那怎么将表单类的实例传给模板呢?

1)因为表单出现在文章详情页,一种想法是修改文章详情页 detail 视图函数,在这个视图中实例化一个表单,然后传递给模板。然而这样做的一个缺点就是需要修改 detail 视图函数的代码,而且 detail 视图函数的作用主要就是处理文章详情;

2)一个视图函数最好不要让它做太多杂七杂八的事情。另外一种想法是使用自定义的模板标签,我们在 页面侧边栏曾经使用自定义模板标签,这里我们使用自定义模板标签的方法,来渲染表单页面。

在 comments\ 文件夹下新建一个 templatetags 文件夹,然后创建 init.py 文件使其成为一个包,再创建一个 comments_extras.py 文件用于存放模板标签的代码。然后我们定义一个 inclusion_tag 类型的模板标签,用于渲染评论表单。

文件位置:comments\comments_extras.py

from django import template
from ..forms import CommentFormregister = template.Library()@register.inclusion_tag('comments/inclusions/_form.html', takes_context=True)
def show_comment_form(context, post, form=None):if form is None:form = CommentForm()return {'form': form,'post': post,}

从定义可以看到,show_comment_form 模板标签使用时会接受一个 post(文章 Post 模型的实例)作为参数,同时也可能传入一个评论表单 CommentForm 的实例 form,如果没有接受到评论表单参数,模板标签就会新创建一个 CommentForm 的实例(一个没有绑定任何数据的空表单)传给模板,否则就直接将接受到的评论表单实例直接传给模板,这主要是为了复用已有的评论表单实例。

然后在 templates/comments/inclusions 目录下(没有就新建)新建一个 _form.html 模板,写上代码:

<form action="{% url 'comments:comment' post.pk %}" method="post" class="comment-form">{% csrf_token %}<div class="row"><div class="col-md-4"><label for="{{ form.name.id_for_label }}">{{ form.name.label }}</label>{{ form.name }}{{ form.name.errors }}</div><div class="col-md-4"><label for="{{ form.email.id_for_label }}">{{ form.email.label }}</label>{{ form.email }}{{ form.email.errors }}</div><div class="col-md-4"><label for="{{ form.url.id_for_label }}">{{ form.url.label }}</label>{{ form.url }}{{ form.url.errors }}</div><div class="col-md-12"><label for="{{ form.text.id_for_label }}">{{ form.text.label }}</label>{{ form.text }}{{ form.text.errors }}<button type="submit" class="comment-btn">发表</button></div></div>    <!-- row -->
</form>

然后我们就可以在 detail.html 中使用这个模板标签来渲染表单了,注意在使用前记得先 {% load comments_extras %} 这个模块。而且为了避免可能的报错,最好重启一下开发服务器。

{% extends 'base.html' %}
{% load comments_extras %}
...<h3>发表评论</h3>
{% show_comment_form post %}

6.“评论”视图函数

当用户提交表单中的数据后,django 需要调用相应的视图函数来处理这些数据,下面开始写我们视图函数处理逻辑:

from blog.models import Post
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_POSTfrom .forms import CommentForm@require_POST
def comment(request, post_pk):# 先获取被评论的文章,因为后面需要把评论和被评论的文章关联起来。# 这里我们使用了 django 提供的一个快捷函数 get_object_or_404,# 这个函数的作用是当获取的文章(Post)存在时,则获取;否则返回 404 页面给用户。post = get_object_or_404(Post, pk=post_pk)# django 将用户提交的数据封装在 request.POST 中,这是一个类字典对象。# 我们利用这些数据构造了 CommentForm 的实例,这样就生成了一个绑定了用户提交数据的表单。form = CommentForm(request.POST)# 当调用 form.is_valid() 方法时,django 自动帮我们检查表单的数据是否符合格式要求。if form.is_valid():# 检查到数据是合法的,调用表单的 save 方法保存数据到数据库,# commit=False 的作用是仅仅利用表单的数据生成 Comment 模型类的实例,但还不保存评论数据到数据库。comment = form.save(commit=False)# 将评论和被评论的文章关联起来。comment.post = post# 最终将评论数据保存进数据库,调用模型实例的 save 方法comment.save()# 重定向到 post 的详情页,实际上当 redirect 函数接收一个模型的实例时,它会调用这个模型实例的 get_absolute_url 方法,# 然后重定向到 get_absolute_url 方法返回的 URL。return redirect(post)# 检查到数据不合法,我们渲染一个预览页面,用于展示表单的错误。# 注意这里被评论的文章 post 也传给了模板,因为我们需要根据 post 来生成表单的提交地址。context = {'post': post,'form': form,}return render(request, 'comments/preview.html', context=context)

首先视图函数被 require_POST 装饰器装饰,从装饰器的名字就可以看出,其作用是限制这个视图只能通过 POST 请求触发,因为创建评论需要用户通过表单提交的数据,而提交表单通常都是限定为 POST 请求,这样更加安全。

另外我们使用了 redirect 快捷函数。这个函数位于 django.shortcuts 模块中,它的作用是对 HTTP 请求进行重定向(即用户访问的是某个 URL,但由于某些原因,服务器会将用户重定向到另外的 URL)。redirect 既可以接收一个 URL 作为参数,也可以接收一个模型的实例作为参数(例如这里的 post)。

如果接收一个模型的实例,那么这个实例必须实现了 get_absolute_url 方法,这样 redirect 会根据 get_absolute_url 方法返回的 URL 值进行重定向,如下:
在这里插入图片描述
与此同时,视图函数中还对读者输入的数据格式进行判断,如果提交的数据合法,我们就将评论数据保存到数据库,否则说明用户提交的表单包含错误,我们将渲染一个 preview.html 页面,来展示表单中的错误,以便用户修改后重新提交。

文件位置:templates/comments/preview.html

{% extends 'base.html' %}
{% load comment_extras %}{% block main %}{% show_comment_form post form %}
{% endblock main %}

这里还是使用 show_comment_form 模板标签来展示一个表单,然而不同的是,这里我们传入由视图函数 comment 传来的绑定了用户提交的数据的表单实例 form,而不是渲染一个空表单。

因为视图函数 comment 中的表单实例是绑定了用户提交的评论数据,以及对数据进行过合法性校验的表单,因此当 django 渲染这个表单时,会连带渲染用户已经填写的表单数据以及数据不合法的错误提示信息,而不是一个空的表单了。

例如下图,我们提交的数据中 email 格式不合法,表单校验了数据格式,然后渲染错误提示:

在这里插入图片描述

7.绑定 URL

在 comment 应用中再建一个 urls.py 文件,写上 URL 模式:

文件位置: comments/urls.py

from django.urls import pathfrom . import viewsapp_name = 'comments'
urlpatterns = [path('comment/<int:post_pk>', views.comment, name='comment'),
]

然后在项目的 blogproject\ 目录的 urls.py 里包含 comments\urls.py 这个文件:
文件位置:blogproject/urls.py

from django.contrib import admin
from django.urls import path,includeurlpatterns = [path(r'admin/', admin.site.urls),path(r'', include('blog.urls')),path(r'', include('comments.urls')),
]

8.向读者发送是否“评论”成功的状态

第六部分中,测试提交评论功能时,尝试输入非法格式的数据,例如将邮箱输入为 xxx@xxx,那么评论视图在校验表单数据合法性时,发现邮箱格式不符,就会渲染 preview 页面,展示表单中的错误,将邮箱修改为正确的格式后,再次点击发表,页面就跳转到了被评论文章的详情页,说明视图正确执行了保存表单数据到数据库的逻辑。

不过这里有一点不好的地方就是,评论成功后页面直接跳转到了被评论文章的详情页,没有任何提示,用户也不知道评论究竟有没有真的成功。这里我们使用 django 自带的 messages 应用来给用户发送评论成功或者失败的消息。

django 默认已经为我们做好了 messages 的相关配置,直接用即可。

两个地方需要发送消息,第一个是当评论成功,即评论数据成功保存到数据库后,因此在 comment 视图中加一句。

from django.contrib import messagesif form.is_valid():...# 最终将评论数据保存进数据库,调用模型实例的 save 方法comment.save()messages.add_message(request, messages.SUCCESS, '评论发表成功!', extra_tags='success')return redirect(post)

这里导入 django 的 messages 模块,使用 add_message 方法增加了一条消息.

消息的第一个参数是当前请求,因为当前请求携带用户的 cookie,django 默认将详细存储在用户的 cookie 中。

第二个参数是消息级别,评论发表成功的消息设置为 messages.SUCCESS,这是 django 已经默认定义好的一个整数,消息级别也可以自己定义。

紧接着传入消息的内容,最后 extra_tags 给这条消息打上额外的标签,标签值可以在展示消息时使用,比如这里我们会把这个值用在模板中的 HTML 标签的 class 属性,增加样式。

同样的,如果评论失败了,也发送一条消息:

# 检查到数据不合法,我们渲染一个预览页面,用于展示表单的错误。
# 注意这里被评论的文章 post 也传给了模板,因为我们需要根据 post 来生成表单的提交地址。
context = {'post': post,'form': form,
}
messages.add_message(request, messages.ERROR, '评论发表失败!请修改表单中的错误后重新提交。', extra_tags='danger')

发送的消息被缓存在 cookie 中,然后我们在模板中获取显示即可。显示消息比较好的地方是在导航条的下面,我们在模板 base.html 的导航条代码下增加如下代码:

文件位置:templates/base.html

<header>...
</header>
{% if messages %}{% for message in messages %}<div class="alert alert-{{ message.tags }} alert-dismissible" role="alert"><button type="button" class="close" data-dismiss="alert" aria-label="Close"><spanaria-hidden="true">&times;</span></button>{{ message }}</div>{% endfor %}
{% endif %}

这里 django 会通过全局上下文自动把 messages 变量传给模板,这个变量里存储我们发送的消息内容,然后就是循环显示消息了。这里我们使用了 bootstrap 的一个 alert 组件,为其设置不同的 class 会显示不同的颜色,所以之前添加消息时传入的 extra_tags 就派上了用场。比如这里 alert-{{ message.tags }},当传入的是 success 时,类名就为 alert-success,这时显示的消息背景颜色就是绿色,传入的是 dangerous,则显示的就是红色。

评论发布成功和失败的消息效果如下图:
在这里插入图片描述

9.详情页底部显示“评论”内容

为了不改动已有的视图函数的代码,评论数据我们也使用自定义的模板标签来实现。

文件位置:comments\comments_extras.py

@register.inclusion_tag('comments/inclusions/_list.html', takes_context=True)
def show_comments(context, post):comment_list = post.comment_set.all().order_by('-created_time')comment_count = comment_list.count()return {'comment_count': comment_count,'comment_list': comment_list,}

文件位置: templates/comments/inclusions/_list.html

<h3>评论列表,共 <span>{{ comment_count }}</span> 条评论</h3>
<ul class="comment-list list-unstyled">{% for comment in comment_list %}<li class="comment-item"><span class="nickname">{{ comment.name }}</span><time class="submit-date" datetime="{{ comment.created_time }}">{{ comment.created_time }}</time><div class="text">{{ comment.text|linebreaks }}</div></li>{% empty %}暂无评论{% endfor %}
</ul>

最后在detail.html 中,将此前占位用的评论模板替换为模板标签渲染的内容:

<h3>发表评论</h3>
{% show_comment_form post %}
<div class="comment-list-panel">{% show_comments post %}
</div>

访问文章详情页,即可看到已经发表的评论列表。效果展示如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/452483.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python与android交互,Android客户端与Python服务器端的简单通信

最近在做一个APP&#xff0c;需要与服务器通信&#xff0c;一点一点的尝试&#xff0c;记录一下。本文使用了OkHttp和Flask框架。Android客户端&#xff1a;实现功能输入完点击OK按钮后会toast成功的信息。Python服务端&#xff1a;各部分代码如下&#xff1a;activity_main.xm…

云栖科技评论第48期:前沿科技对世界的改造 我们这代人只完成了1%

1、数字经济版图呈中美双分趋势 日本IT行业为前景担忧 数字经济版图呈中美双分趋势 日本IT行业为前景担忧 【新闻摘要】《日本经济新闻》日前刊文称&#xff0c;数字经济的势力版图呈现中国和美国两强双分的趋势明显&#xff0c;这意味着日本可能不得不使用中美的技术&#xff…

CentOS下添加Root权限用户‘超级用户’方法(xxx is not in the sudoers file.This incident will be reported.的解决方法)

文章目录1.添加普通用户2.添加sudo文件的写权限3.编辑sudoers文件4.撤销sudoers文件写权限1.添加普通用户 [rootserver ~]# useradd fxd //添加一个名为fxd的用户 [rootserver ~]# passwd fxd //修改密码 Changing password for user chenjiafa. New UNIX password: //在这里输…

android打印intent flag,Android flag详解

Android flag详解Android flag详解一.Flag标志位在阅读源码的时候经常发现有一些标志属性使用一些位操作来判断是否具有该标志&#xff0c;增加标志或者去除标志。如&#xff1a;二.addFlags (int flags)和setFlags (int flags)区别Public Intent addFlags (int flags)增加额外…

开启市场新格局 且看新华三计算与存储新品发布会

全球领先的新IT解决方案领导者新华三将于8月31日在北京钓鱼台国宾馆举办“新IT 新动能 新格局 新华三下一代计算与存储发布会”&#xff0c;重磅发布服务器和存储的全线新品。 2017年初&#xff0c;新华三集团正式推出了“应用驱动 云领未来”的新IT战略。在这一战略指引下&…

龟兔赛跑问题

题目 乌龟与兔子进行赛跑&#xff0c;跑场是一个矩型跑道&#xff0c;跑道边可以随地进行休息。乌龟每分钟可以前进3米&#xff0c;兔子每分钟前进9米&#xff1b;兔子嫌乌龟跑得慢&#xff0c;觉得肯定能跑赢乌龟&#xff0c;于是&#xff0c;每跑10分钟回头看一下乌龟&#x…

Unity4.6证书激活问题

第一次运行的激活问题安装好Unity4.6之后首次启动会自动连网&#xff0c;但是会出现error loading page错误&#xff0c;SSL peer certificate or SSH remote key was not OK。如下图所示&#xff1a; 点OK就退出了。再启动一遍也是一样的。 这时可以先暂时断一下网&#xff0c…

Readhat中挂载yum源

文章目录1.链接物理镜像2.查看原始挂载目录3.挂载4.新建iso.repo5.查看挂载目录1.链接物理镜像 vmware控制台中&#xff0c;勾选设备状态成“已连接”&#xff0c;会在桌面生成镜像图标。 2.查看原始挂载目录 输入以下指令&#xff1a; [rootlocalhost ~]# df -h Filesystem…

知其所以然地学习(以算法学习为例)

其实下文的绝大部分内容对所有学习都是同理的。只不过最近在正儿巴经地学算法&#xff0c;而后者又不是好啃的骨头&#xff0c;所以平时思考总结得就自然要比学其它东西要多一些。 问题&#xff1a;目前几乎所有的算法书的讲解方式都是欧几里德式的、瀑布式的、自上而下的、每…

【Java】 大话数据结构(1) 线性表之顺序存储结构

本文根据《大话数据结构》一书&#xff0c;实现了Java版的顺序存储结构。 顺序存储结构指的是用一段地址连续的存储单元一次存储线性表的数据元素&#xff0c;一般用一维数组来实现。 书中的线性表抽象数据类型定义如下&#xff08;第45页&#xff09;&#xff1a; 实现程序&am…

Readhat中升级openssh

1.挂载yum源&#xff08;镜像&#xff09; 如何挂载yum源&#xff08;镜像&#xff09;&#xff0c;在本人的另外一篇博客中&#xff0c;请戳这里 2.安装升级所需依赖 由于升级ssh需要安装依赖&#xff0c;这里提前将所需依赖安装 指令&#xff1a; yum install -y zlib-dev…

Readhat中作安全基线

文章目录1.新建文件夹并上传脚本2.修改脚本中设置root2用户的密码字段3.添加脚本的可执行权限4.执行脚本5.尝试使用root2登录验证1.新建文件夹并上传脚本 在根目录下的/openssh8&#xff08;没有则新建&#xff09;中上传脚本&#xff1a; 2.修改脚本中设置root2用户的密码字…

WordPress插件开发-创建、停用、删除插件

插件存放目录 wp-content/plugins 创建一个插件 在plugins创建一个文件插件文件夹&#xff0c;命名最好加前缀&#xff0c;这个前缀可以使用你的名字或者你自己的域名&#xff0c;防止插件和别人重名&#xff0c;再创建一个PHP文件名字和你的插件名一样。我这里创建一个名为 yg…

Diango博客--11.Nginx + Gunicorn + Supervisor 方式部署

文章目录0.部署前准备1.创建一个超级用户2.更新 SQLite33.安装 Python3 、pip3.6以及 Pipenv4.部署代码5.使用 Gunicorn6.启动 Nginx 服务器7.配置 Nginx8.关闭 DEBUG 模式&#xff0c;收集静态文件9.使用 Supervisor管理 Gunicorn 进程10.使用 CDN 加快 Bootstrap 和 jQuery 的…

StringBuilder类

1.1 StringBuilder类概述StringBuilder:是一个可变的字符串。字符串缓冲区类。 String和StringBuilder的区别&#xff1a;String的内容是固定的StringBuilder的内容是可变的1.1.1 拼接字符串耗费内存原因:每次拼接都会产生新的字符串对象,而利用StringBuilder来拼接字符串自始至…

如何使用搜索技巧来成为一名高效的程序员

没有人是完全独立的孤岛&#xff0c;每个人都是整体的一部分。—— 约翰多恩对于缺乏编程知识的人来说&#xff0c;完全有可能编写一个网页或小程序。如果在用Google搜索相关示例时幸运的话&#xff0c;可以搜到现成的代码。即使是经验丰富的程序员&#xff0c;通常也会为了节省…

linux系统优化思路

文章目录一. 影响linux性能的因素1.系统硬件资源2.操作系统相关资源3.应用程序软件资源二. 分析系统性能涉及的人员1.Linux运维人员2.系统架构设计人员3.软件开发人员三. 系统性能分析工具四.思路总结一. 影响linux性能的因素 1.系统硬件资源 1&#xff09;cpu 2&#xff09;…

30分钟,让你成为一个更好的程序员

我相信激励是非常重要的。这也是为什么我常常把时间管理&#xff08;这些书激励我不管改进我的时间管理方法&#xff09;的书和软件开发拿出来看看。我最近刚看完一本 书&#xff0c;“Apprenticeship Patterns: Guidance for the Aspiring Software Craftsman”。这本书推动我…

如何启用计算机的远程服务,远程桌面服务,教您怎么打开远程桌面服务

远程桌面可以通过控制另一台电脑的功能&#xff0c;让网络管理员可以在家中安全的控制单位的服务器&#xff0c;而且这个功能是系统内置的&#xff0c;所以比使用其他第三方的远程控制工具更方便。那么我的重点来了&#xff0c;我今天就教你们怎么打开桌面远程服务&#xff0c;…

pipenv相关指令

pip install pipenv 安装pipenv pipenv install 创建虚拟环境 &#xff08;项目根目录下&#xff09; pipenv shell     进入虚拟环境&#xff08;如果不存在&#xff0c;则创建并进入虚拟环境&#xff09; pipenv install flask   安装模块 pipenv uninstall flask 卸…