上篇我们实现了用户注册功能,至此用户管理模块已经全部完成。
接下来我们来实现博客的另一重要功能:文章评论。
有了文章管理的知识积累,实现评论管理功能就比较轻松了。
按照MTV的模型分别编写对应的模块。
创建App
通过如下代码新建一个用于评论管理的App:
python manage.py startapp comment
注册App
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'article',
'userprofile',
# 新增'comment'代码,激活app
'comment',
]
为了显示发表评论的时间为中国时间,修改时区设置TIME_ZONE
为上海的时区。
TIME_ZONE = 'Asia/Shanghai'
定义模型
from django.db import models
from django.contrib.auth.models import User
from article.models import Article
# 博文的评论
class Comment(models.Model):
article = models.ForeignKey(Article,
on_delete=models.CASCADE,
related_name='comments'
)
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='comments'
)
body = models.TextField()
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created',)
def __str__(self):
return self.body[:20]
在模型中我们定义了两个外键
如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。
article
是被评论的文章user
是评论的发布者
对数据进行迁移
创建表单
新建一个评论表单
from django import forms
from .models import Comment
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['body']
评论表中,有两个字段属于外键,针对外键字段,Django
的内部逻辑是可以自动和外部数据表关联生成。
我们在表单中实际只需要前台处理body
字段就可以了。
创建RRL
在django4blog/urls.py
新增评论管理URL:
from django.contrib import admin
from django.urls import path, re_path
# 引入app视图
import article.views
import userprofile.views
import comment.views
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/', article.views.hello),
re_path(r'^$', article.views.article_list),
path('list/', article.views.article_list, name='list'), # 展示文章
path('detail/<int:id>/', article.views.article_detail, name='detail'), # 文章详情
path('create/', article.views.article_create, name='create'), # 写文章
path('delete/<int:id>/', article.views.article_delete, name='delete'),# 删除文章
path('update/<int:id>/', article.views.article_update, name='update'), # 更新文章
path('login/', userprofile.views.user_login, name='login' ),
path('logout/', userprofile.views.user_logout, name='logout' ),
path('register/', userprofile.views.user_register, name='register' ),
# 增加评论管理
path('post-comment/<int:article_id>/', comment.views.post_comment, name='post_comment' ),
]
创建视图
评论的视图函数如下:
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from article.models import Article
from .forms import CommentForm
# 文章评论
@login_required(login_url='/login/')
def post_comment(request, article_id):
article = get_object_or_404(Article, id=article_id)
# 处理 POST 请求
if request.method == 'POST':
comment_form = CommentForm(request.POST)
if comment_form.is_valid():
new_comment = comment_form.save(commit=False)
new_comment.article = article
new_comment.user = request.user
new_comment.save()
return redirect(article)
else:
return HttpResponse("表单内容有误,请重新填写。")
# 处理错误请求
else:
return HttpResponse("发表评论仅接受POST请求。")
get_object_or_404():
它和Model.objects.get()
的功能基本是相同的。区别是在生产环境下,如果用户请求一个不存在的对象时,Model.objects.get()
会返回Error 500
(服务器内部错误),而get_object_or_404()
会返回Error 404。相比之下,返回404错误更加的准确。
redirect():
返回到一个适当的url
中:即用户发送评论后,重新定向到文章详情页面。当其参数是一个Model
对象时,会自动调用这个Model
对象的get_absolute_url()
方法。因此接下来马上修改article
的模型。
修改文章模型
文章模型article/models.py
中添加get_absolute_url()
方法:
# 博客文章数据模型
class Article(models.Model):
# 文章id,主键
id = models.AutoField(primary_key=True)
# 文章作者
author = models.CharField(max_length=100)
# 文章标题,models.CharField 为字符串字段,用于保存较短的字符串,比如标题
title = models.CharField('标题',max_length=100)
# 文章正文,保存大量文本使用 TextField
body = models.TextField('文章正文')
# 文章创建时间,参数 default=timezone.now 指定其在创建数据时将默认写入当前的时间
created = models.DateTimeField(default=timezone.now)
# 文章更新时间,参数 auto_now=True 指定每次数据更新时自动写入当前时间
updated = models.DateTimeField(auto_now=True)
# 获取文章地址
def get_absolute_url(self):
return reverse('detail', args=[self.id])
通过reverse()
方法返回文章详情页面的url,实现了路由重定向。
修改文章详情视图
评论模块需要在文章详情页面展示,所以必须把评论模块的上下文也传递到模板中。
因此修改article/views.py
中的article_detail()
:
# 文章详情
def article_detail(request,id):
# 取出相应的文章
article = Article.objects.get(id=id)
# 取出文章评论
comments = Comment.objects.filter(article=id)
# 需要传递给模板的对象
context = {'article': article, 'comments': comments}
# 载入模板,并返回context对象
return render(request, 'article/detail.html', context)
修改文章详情模板
<!-- extends表明此页面继承自 base.html 文件 -->
{% extends "base.html" %}
{% load static %}
<!-- 写入 base.html 中定义的 title -->
{% block title %}
文章详情
{% endblock title %}
<!-- 写入 base.html 中定义的 content -->
{% block content %}
<!-- 文章详情 -->
<div class="container">
<!-- <div class="row">-->
<!-- 标题及作者 -->
<h1 class="col-12 mt-4 mb-4">{{ article.title }}</h1>
<div class="col-12 alert alert-primary">
<div class="col-12">
<a>作者:{{ article.author }}</a>
 
<a>{{ article.created|date:'Y-m-d H:i:s' }}</a>
 
<a href="#" data-bs-toggle="modal" data-bs-target="#myModal">删除文章</a>
<!-- 新增一个隐藏的表单 -->
<form
style="display:none;"
id="safe_delete"
action="{% url "delete" article.id %}"
method="POST"
>
{% csrf_token %}
<button type="submit">发送</button>
</form>
 
<a href="{% url "update" article.id %}">编辑文章</a>
</div>
</div>
<!-- 文章正文 -->
<div class="col-12">
<p>{{ article.body }}</p>
</div>
<!-- </div>-->
<div class="container">
<div class="col-12">
<!-- 发表评论 -->
<hr>
{% if user.is_authenticated %}
<div>
<form
action="{% url "post_comment" article.id %}"
method="POST"
>
{% csrf_token %}
<div class="form-group">
<label for="body">
<strong>
我也要发言:
</strong>
</label>
<textarea
type="text"
class="form-control"
id="body"
name="body"
rows="2"></textarea>
</div>
<br>
<!-- 提交按钮 -->
<button type="submit" class="btn btn-primary ">发送</button>
</form>
</div>
<br>
{% else %}
<br>
<h5 class="col-12 ">
请<a href="{% url 'login' %}">登录</a>后回复
</h5>
</h5>
<br>
{% endif %}
</div>
</div>
<!-- 显示评论 -->
<h4>共有{{ comments.count }}条评论</h4>
<div>
{% for comment in comments %}
<hr>
<p>
<strong style="color: pink">
{{ comment.user }}
</strong> 于
<span style="color: green">
{{ comment.created|date:"Y-m-d H:i:s" }}
</span> 时说:
</p>
<pre style="font-family: inherit; font-size: 1em;">
{{ comment.body }}</pre>
{% endfor %}
</div>
</div>
</div>
{% endblock content %}
- 表单组件中的
action
指定数据提交到哪个url中 - 显示评论中的
comments.count
是模板对象中内置的方法,对包含的元素进行计数 |date:"Y-m-d H:i :s":
管道符你已经很熟悉了,用于给对象“粘贴”某些属性或功能。这里用于格式化日期的显示方式。<pre>
定义预格式化的文本,在我们的项目中最关键的作用是保留空格和换行符。该标签会改变文字的字体、大小等,因此用style属性重新定义相关内容。尝试将<pre>
替换为div
,输入多行文本试试效果。
运行测试
运行之后,查看详情页,如果没登录会提示需要登录后回复评论。
登录后发布评论
结语
至此我们已经完成了评论管理的核心功能,发布和显示,其实部分网站还提供评论的修改和删除的功能。
实现过程和文章的修改和删除差不多,大家可以自行完成相关的开发。
我们博客网站的核心功能模块,文章,用户和评论部分基本已经完成了。
接下来我们将完成博客的一些比较实用的小功能点。
比如,分页,排序以及搜索等。