本文为参考追梦人物老师的博客学习记录贴。
本篇为博客开发评论功能,由于功能相对独立,在项目下新建一个应用comments 。
新建comments应用
回顾新建应用的过程,在项目路径下,进入cmd界面,使用命令行新建一个项目。
快速进行项目路径下的终端
进入虚拟环境
新建comments应用
创建成功
一些初始设置
在settings.py里注册应用
admin后台中应用 的显示名称,在 comments\apps.py 里
from django.apps import AppConfig
class CommentsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'comments'
verbose_name = '评论应用'
编写数据模型
创建Comment评论模型。首先需要评论者的一些信息,以及评论时间和评论内容。此外还必须与被评论文章进行关联,知道当前评论属于哪篇文章。
进入 comments\models.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())
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
在comments\admin.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 表示展示列表中出现的字段。
字段fields 表示编辑页面中需要编辑的字段。
django中的表单
表单用于浏览器收集用户信息,并提交到服务器。一般使用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):
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表单对象作为参数,如果表单对象为空则实例化一个对象。如果存在则直接传递给模板。
comments\templatetags\comment_extras.py
from django import template
from comments.forms import CommentForm
register = template.Library()
@register.inclusion_tag(r'comment\inclusions\_form.html',takes_context=True)
def show_comments_form(context, post, form=None):
if form is None:
form = CommentForm()
return {
'form': form,
'post': post,
}
创建标签模板进行渲染
templates\comment\inclusions\_form.html
<form action="{% url 'comments:comment' post.pk %}" method="post">
{% 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-10">
<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>
</form>
注意: <form>表单 创建表单标签,向url提交用户信息。 action: 提交的url method: 请求方式 csrf_token CSRF 令牌,用于防护跨站请求攻击(CSRF) <label> 标签 label 标签为某个表单元素定义标注(标签),提高用户体验 作用: 用于绑定一个表单元素, 当点击label标签的时候, 被绑定的表单元素就会获得输入焦点。当我们鼠标点击 label标签里面的文字时, 光标会定位到指定的表单里面。 增加label标签后,点击文本框可以选中,点击框前的文字"姓名"也可以选择进行输入。 使用: 用for 属性与表单的id关联,用于规定 label 与哪个表单元素绑定。
<label for="name1">姓名</label>
<input type="text" name="name" id="name1">
{{form.name.label}} 表单字段的显示名,根据表单对应的模型的字段的verbose_name属性进行提取。 {{form.name}} 表单字段,在模板中自动渲染成表单html代码 {{form.name.errors}} 表单填写过程中的错误自动保存到了errors中,在模板中会自动将错误渲染并展示出来。
templates\blog\detail.html
{% load comment_extras %}
...
<div class="comment-list-panel">
{% show_comments post %}
</div>
...
在当前页面,我们传递了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
def comment(request, pk):
post = get_object_or_404(Post,pk=pk)
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.post = post
comment.save()
messages.add_message(request, messages.SUCCESS,'评论发表成功!',extra_tags='success')
return redirect(post)
messages.add_message(request,messages.ERROR,'评论失败!请修改表单中的错误后再重新提交。', extra_tags='danger')
context = {
'post': post,
'form': form,
}
return render(request, r'comment\preview.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作为全局变量,可以在模板的任何位置进行获取(如果进行了赋值)
在详情页面进行消息提醒:
<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"><span
aria-hidden="true">×</span></button>
{{ message }}
</div>
{% endfor %}
{% endif %}
url的设置
comment应用下的url绑定
comments\urls.py
from django.urls import path
from . import views
app_name = 'comments'
urlpatterns = [
path('comment/<int:pk>', views.comment, name='comment')
]
在项目目录下进行注册
django1\urls.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'))
]
显示评论内容
同样使用模板标签进行局部页面渲染,展示评论内容。 首先编写标签函数:
comments\templatetags\comment_extras.py
@register.inclusion_tag(r'comment\inclusions\_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对象。适用于有外键关联 的两个模型,由一找多 。
标签模板中的渲染
<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>
其中{{comment.text|linebreaks}}中的linebreaks 表示换行过滤器。
使用模板标签:
<div class="comment-list-panel">
{% show_comments post %}
</div>
一些其他的完善点
在模型中指定排序: 对于需要经常排序输出的模型数据,我们可以在 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"
<section class="comment-area" id="comment-area">
<hr>
<h3>发表评论</h3>
{% show_comments_form post %}
<div class="comment-list-panel">
{% show_comments post %}
</div>
</section>
则跳转链接为 href="{{post.get_absolute_url}}#comment-area" 。
|