本文为参考追梦人物老师的博客学习记录贴。
本篇为博客开发评论功能,由于功能相对独立,在项目下新建一个应用comments。
新建comments应用回顾新建应用的过程,在项目路径下,进入cmd界面,使用命令行新建一个项目。
快速进行项目路径下的终端
进入虚拟环境
新建comments应用
一些初始设置创建成功
在settings.py里注册应用
admin后台中应用的显示名称,在 commentsapps.py 里
from django.apps import AppConfig
class CommentsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'comments'
verbose_name = '评论应用' # 在admin后台的显示名
编写数据模型
创建Comment评论模型。首先需要评论者的一些信息,以及评论时间和评论内容。此外还必须与被评论文章进行关联,知道当前评论属于哪篇文章。
- 编写模型
进入 commentsmodels.py中
from django.db import models
from django.utils import timezone
class 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()) # default:创建时添加 save函数:保存到数据库时添加
post = models.ForeignKey('blog.Post', verbose_name='文章', on_delete=models.CASCADE) # 外键,品论的文章
class Meta:
verbose_name = '评论'
verbose_name_plural = verbose_name
def __str__(self):
return '{}:{}'.format(self.name, self.text[:20])
注意:
- 所有的模型字段都接受一个verbose_name的参数,以第一位置参数或者"verbose_name="的形式显示指定,表示该字段对外的显示名称。
在admin后台中字段的显示名称:
>
在表单中作为字段的label值:
- 字段的default参数表示在新建一个实例时,自动为该字段赋值,只赋一次值;
而模型的save()函数同意可以指定字段的保存值。不同之处在于save()函数在每次向数据库保存数据时都生效。
class Comment(models.Model):
...
created_time = models.DateTimeField('创建时间', default=timezone.now())
...
def save(self, *args, **kwargs):
...
self.modified_time = timezone.now()
...
super().save(*args, **kwargs)
- class Meta表示模型的内部类,为模型添加一些功能,或者指定一些标准。如:
指定该模型的数据库表单名字
db_table = ‘axf_wheel’
指定admin后台中模型的显示名称
verbose_name = ‘评论模型’
verbose_name_plural = verbose_name
- str()方法,指定查询模型后的返回值。
- 模型建立后,进行数据迁移
> python manage.py makemigrations > python manage.py migrate
- 将模型注册到admin后台中
在commentsadmin.py中进行comment模型的注册。
from django.contrib import admin
from comments.models import Comment
class CommentAdmin(admin.ModelAdmin):
list_display = ['name','email','url','post','created_time']
fields = ['name','email','url','text','post']
admin.site.register(Comment,CommentAdmin)
模型管理类CommentAdmin继承自admin.ModelAdmin类。
字段list_display 表示展示列表中出现的字段。
django中的表单字段fields 表示编辑页面中需要编辑的字段。
表单用于浏览器收集用户信息,并提交到服务器。一般使用POST请求方式。
与常规的在HTML页面编辑表单格式不同,django使用form类进行表单的渲染。
- 继承forms.Form类或者forms.ModelForm类生成表单类
from django import forms
from .models import Comment
"""
通过调用这个表单类的一些方法和属性,django 将自动为我们创建常规的表单代码,而不是在html文件中编辑,类似于ORM系统。
"""
"""
那么怎么展现一个表单呢?django 会根据表单类的定义自动生成表单的 HTML 代码,
我们要做的就是实例化这个表单类,然后将表单的实例传给模板,让 django 的模板引擎来渲染这个表单。
"""
class CommentForm(forms.ModelForm):
# 表单类,继承自forms
# forms.ModelForm表单有对应的数据模型
# forms.Form表单可以没有对应的数据模型
class Meta: # 元类,用于指定父类的功能或标准
model = Comment # 表单对应的数据模型
fields = ['name', 'email', 'url', 'text'] # 用于展示的字段
使用表单类
- 首先导入基类forms,其下有两个派生类forms.ModelForm和forms.Form。其中forms.ModelForm表单有对应的数据模型,而forms.Form表单可以没有对应的数据模型。
- 在元类class Meta中,指定表单对接的数据模型,以及表单需要收集的字段。
- 在django中表单的创建原理:系统根据表单类的定义自动生成表单的 HTML 代码,我们要做的就是实例化这个表单类,然后将表单实例传给模板,然后django 的模板引擎会自动渲染这个表单。
有了表单类CommentForm后,接下来要做的就是实例化一个表单类对象,然后将其传递到前端。在前端使用到这个表单类的地方就会自动渲染为一个表单。
在本文中我们使用自定义模板标签来渲染一个局部的 HTML评论 页面。
创建用于评论展示的模板标签- 模板标签函数
创建标签函数的python包
注册模板标签函数
标签函数使用post对象、form表单对象作为参数,如果表单对象为空则实例化一个对象。如果存在则直接传递给模板。
commentstemplatetagscomment_extras.py
from django import template
from comments.forms import CommentForm
register = template.Library()
@register.inclusion_tag(r'commentinclusions_form.html',takes_context=True)
def show_comments_form(context, post, form=None):
if form is None:
form = CommentForm()
# 如果表单已存在,就复用已有的评论表单实例
return {
'form': form,
'post': post,
}
- 渲染标签模板
创建标签模板进行渲染
templatescommentinclusions_form.html
注意:
{{form.name.label}}
表单字段的显示名,根据表单对应的模型的字段的verbose_name属性进行提取。
{{form.name}}
表单字段,在模板中自动渲染成表单html代码
{{form.name.errors}}
表单填写过程中的错误自动保存到了errors中,在模板中会自动将错误渲染并展示出来。
- 使用模板标签进行评论表单的展示
templatesblogdetail.html
{% load comment_extras %}
...
{% show_comments post %}
...
在当前页面,我们传递了post对象,而没有form对象,因此表单内容为空,会实例化一个空白表单对象并展示。
处理表单数据的视图函数表单显示成功
- 首先获取评论针对的文章
- 然后通过request.POST请求获取表单数据并封装到表单对象中
- 检验表单数据的合法性
- 将表单数据封装为模型数据,保存到数据库中
- 表单数据不合法则渲染一个修改页面,重新提交
from django.contrib import messages
from django.shortcuts import render, redirect
from blog.models import Post
from django.shortcuts import get_object_or_404
from .forms import CommentForm
# Create your views here.
def comment(request, pk):
post = get_object_or_404(Post,pk=pk) # 根据pk查找post对象
form = CommentForm(request.POST) # 通过提交的POST数据生成表单对象
if form.is_valid():
comment = form.save(commit=False) # 表单对象的save方法将生成对应的模型实例,commit表示是否将模型数据保存数据库
comment.post = post # 提交的POST数据中并没有post的数据
comment.save() # 最后保存到数据库
# 消息被缓存在 cookie 中,然后我们在模板中获取显示即可。
messages.add_message(request, messages.SUCCESS,'评论发表成功!',extra_tags='success')
return redirect(post) # 重定向一个模型时,将调用这个模型的get_absolute_url 方法
# 如果没有重定向,说明表单有误,则渲染一个错误页面,展示错误详情
messages.add_message(request,messages.ERROR,'评论失败!请修改表单中的错误后再重新提交。', extra_tags='danger')
context = {
'post': post,
'form': form,
}
return render(request, r'commentpreview.html', context=context)
数据的层层传递
form = CommentForm(request.POST) — 将POST的请求数据包装成表单对象
comment = form.save(commit=False) — 将表单对象包装成数据模型
comment.save() —将模型数据保存到数据库中
数据的层层传递需要字段始终保持一致性
messages.add_message(request, messages.SUCCESS,'评论发表成功!',extra_tags='success')
添加全局上下文消息,第一个参数为请求,第二个参数为消息等级,第三个参数为消息内容,第四个参数为额外标签,用于HTML 标签的 class 属性,增加样式。
消息messages作为全局变量,可以在模板的任何位置进行获取(如果进行了赋值)
在详情页面进行消息提醒:
url的设置... {% if messages %} {% for message in messages %} {% endfor %} {% endif %}
comment应用下的url绑定
commentsurls.py
from django.urls import path
from . import views
app_name = 'comments' # 告诉程序,当前的url模块属于哪个应用 --- 试图函数命名空间
urlpatterns = [
path('comment/', views.comment, name='comment')
]
在项目目录下进行注册
django1urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
path('',include('comments.urls'))
]
显示评论内容
同样使用模板标签进行局部页面渲染,展示评论内容。
首先编写标签函数:
commentstemplatetagscomment_extras.py
@register.inclusion_tag(r'commentinclusions_comment.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_list':comment_list,
'comment_count': comment_count,
}
通过传入的post对象筛选关联的评论,即comment数据。然后将评论列表传递给标签模板中进行渲染。
注意:
post.comment_set.all()表示筛选与post关联的所有comment对象。以post出发,寻找与其关联的多个comment,all()方法返回找到的所有comment对象。适用于有外键关联的两个模型,由一找多。
标签模板中的渲染
评论列表,共{{comment_count}}条评论
-
{% for comment in comment_list %}
-
{{comment.name}}
{{comment.text|linebreaks}}
{% empty %}
暂无评论
{% endfor %}
其中{{comment.text|linebreaks}}中的linebreaks表示换行过滤器。
使用模板标签:
一些其他的完善点{% show_comments post %}
在模型中指定排序:
对于需要经常排序输出的模型数据,我们可以在 models.Model 的子类里定义一个名为 Meta 的内部类,通过这个内部类指定一些属性的值来规定这个模型类该有的一些特性,例如在这里我们要指定 Post 的排序方式。首先看到 Post 的代码,在 Post 模型的内部定义的 Meta 类中,指定排序属性 ordering:
class Post(models.Model):
...
class Meta:
verbose_name = '文章'
verbose_name_plural = '文章'
ordering = ['-created_time']
...
这样Post模型的输出就总是按创建时间进行排序输出了。
跳转到页面指定区域:
通过url链接不但可以跳转到指定页面,还可以通过 #id值的方式跳转到指定页面的指定区域
如想要跳转到详情页面的评论区域,评论区的id值为 id="comment-area"
发表评论 {% show_comments_form post %}{% show_comments post %}
则跳转链接为 href="{{post.get_absolute_url}}#comment-area"。



