- blog应用
- 40行左右
- 56行,
- static-files-dirs 新增这一行
- 第二步:准备
- Create your models here.
- 分类、标签、文章
- 分类标签,就一个字段,name
- 每个模型都写,方便后台管理
- 文章,3+2+3=8字段
- 创建文章的后台模型
- 后台注册模型
- 下载静态资源
- 模板
- 第三步
- index 函数
- category 函数
- 获取指定分类,赋值给cate
- 在文章里过滤指定分类出来,赋值给post_list
- 渲染到 index.html页面,给post_list
- tag 函数
- comment应用
- 实例化 模板自定义标签 Library,赋值给 register
- 通过装饰器,注册调用inclusion_tag方法
- 函数, show_comment_form 展示评论表单
按着追梦的教程写博客
地址:https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/63/
blog应用
第一步:设置
创建项目、应用
django-admin startproject Thursdaycd Thursdaydjango-admin startapp blog
配置
- 注册应用
- 自定义tmeplate和static位置
- 默认在应用下的 templates、static文件找。
- 我们要让它在根目录找,所有在根目录创建 templates、static文件夹
```python
import os
40行左右
INSTALLED_APPS = [
'Thursday','blog', # 注册应用
]
56行,
TEMPLATES = [ { ‘BACKEND’: ‘django.template.backends.django.DjangoTemplates’, ‘DIRS’: [os.path.join(BASE_DIR,’templates’)], # 改这里 ‘APP_DIRS’: True, ‘OPTIONS’: { ‘context_processors’: [ ‘django.template.context_processors.debug’, ‘django.template.context_processors.request’, ‘django.contrib.auth.context_processors.auth’, ‘django.contrib.messages.context_processors.messages’, ], }, }, ]
static-files-dirs 新增这一行
STATICFILES_DIRS = [os.path.join(BASE_DIR,’static’)]
<a name="jJPvi"></a>### 函数- 在template文件夹下创建 blog文件夹,创建 index.html文件。- blog应用下 viwes.py写函数处理```pythonfrom django.shortcuts import renderdef index(request):return render(request,'blog/index.html')
页面
就随便写点
- 内容就
星期四 有时候内容太少不显示。 模板写多一点再刷新就有了
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><h3>星期四了</h3><div class="">2222</div></body></html>
路由
主路由 :‘’
子路由:‘’
默认地址,直接让index函数显示index页面, http://127.0.0.1:8000/主路由 ```python from django.contrib import admin from django.urls import path,include
urlpatterns = [ path(‘admin/‘, admin.site.urls), path(‘’,include(‘blog.urls’)),
]
- 子路由- 在blog下创建urls.py 文件```pythonfrom django.urls import pathfrom . import viewsapp_name = 'blog'urlpatterns = [path('',views.index,name='index'),
运行效果
运行 py manage.py runserver
打开地址: http://localhost:8000/
报错正常,第一次要么打漏要么打错。要么先复制粘贴,要多打几次熟练就好了
第二步:准备
模型设计
3模型:Post、Tag、Category
- 要创建3个模型,顺序: 先有 分类
Category、标签Tag、再有Post文章 - 先简单写字段,后面将字段内属性。再写Meta属性、方法 ```python
from django.db import models
from django.utils import timezone from django.contrib.auth.models import User
from django.urls import reverse
Create your models here.
分类、标签、文章
分类标签,就一个字段,name
# 填的最大长度一样, 100
每个模型都写,方便后台管理
# Meta属性, 字段别名 verbore_name# __str__方法,指向自身第一个字段
文章,3+2+3=8字段
# 标题、内容、摘要# 标题, 填写最多70# 内容 text格式# 摘要,可空; 填写最多200# 创建时间、修改时间# 时间日期格式 datetime ,默认现在# 关联的模型, 作者、分类、标签s# 关联模型 的别名必须是 verbose_name ,其他 ‘’就行# 作者,分类都是一对一关系,外键删除-关联都删除# 标签,多对多,可空
class Category(models.Model): name = models.CharField(max_length=100)
class Meta():verbose_name = '分类'verbose_name_plural = verbose_namedef __str__(self) -> str:return self.name
class Tag(models.Model): name = models.CharField(max_length=100)
class Meta():verbose_name = '标签'verbose_name_plural = verbose_namedef __str__(self) -> str:return self.name
class Post(models.Model): title = models.CharField(‘标签’,max_length=200) body = models.TextField(‘内容’) summary = models.CharField(‘摘要’,max_length=200,blank=True)
created_time = models.DateTimeField('创建时间',default=timezone.now)modified_time = models.DateTimeField('修改时间')author = models.ForeignKey(User,on_delete=models.CASCADE,verbose_name='作者')category = models.ForeignKey(Category,on_delete=models.CASCADE,verbose_name='分类')tags = models.ManyToManyField(Tag,blank=True,verbose_name='标签')class Meta():verbose_name = '文章'verbose_name_plural = verbose_namedef __str__(self) -> str:return self.title
django中默认设置的是UTC时区,所以我们数据库中存储时间就是UTC时区的时间<br />django.utils.timeone提供2个方法- now()根据 USE_TZ,时区时间- localtime() ,根据TIME_ZONE ,时间django.contrib是提供功能包- auth.models 提供身份验证的,User就是其中一个<a name="Um3v8"></a>#### 运行迁移校验模型 `py manage.py makemigrations`<br />数据库创建表` py manage.py migate`<br /><a name="ecZvR"></a>#### post模型补齐方法- 写2方法,给文章地址,重写save(修改时间、摘要内容)- 获取绝对地址:get_absolute_url- 就是 detail 页面地址: post/<int:pk>- 反向解析,运行detail函数,关键字参数传递 pk- 重写save- 修改时间 ,保存当前时间。------改文章才保存才触发- 摘要 = 文章内容的前25个字- 编辑文章的格式是 markdown,需要转为html- 再将html的标签除去,留下文本内容```pythonfrom django.db import modelsfrom django.utils import timezonefrom django.contrib.auth.models import Userfrom django.urls import reversefrom django.utils.html import strip_tagsimport markdown#post 多了2个方法。# 获取绝对地址 get_absoult_url# 反向解析,获取blog下detail端点的 地址 ,# 通过关键字参数,将 self.pk 值传递给键 'pk'# 复写 save方法# 更新修改时间,获取当前时间复制给 modified# 实例化 markdown复制给 md# extensions扩展选项: 额外extra、亮高code hilite# 取摘要的一部分# convert方法,将body的内容 md转为html格式,# strip_tags方法,再 取出标签要文本内容# [:54],最后切片要前54个字符# super(),继承保存class Post(models.Model):def get_absolute_url(self): # absolutereturn reverse('blog:detail',kwargs={'pk':self.pk})# 重新保存,修改时间为现在的时间。 这样后台管理才能保存,不然报错def save(self,*args,**kwargs) -> None:self.modified_time = timezone.now() # 是方法 now()md = markdown.Markdown(extensions=['markdown.extensions.extra','markdown.extensions.codehilite',])self.summary = strip_tags(md.convert(self.body))[:54]return super().save(*args,**kwargs)
django.utils除了提供 timeone,还提供其他。如html
strip_tags() 从html标签里提取文本,其他删除
后台数据管理
创建库表,里面没有数据。下面是用官方提供后台管理系统,手动添加内容。
admin
创建超级管理员
- py manage.py createsuperuser
- 账号 admin 邮箱 admin@example.com
- 账号密码都是 admin y
- blog应用下 admin.py 文件注册模型,方便后台增删改查
- list-display:进去后台,能看到的列表内容。 ——-字段不能是多对多关系的
- fields: 添加内容,显示/编辑的字段
- 重写 保存模型
- 就加一行关联, 请求用户 与 作者是同一个 ```python from django.contrib import admin from .models import Post,Category,Tag
创建文章的后台模型
# 列表显示内容 list_display# 显示:标题、时间、分类、作者# 填写显示字段 fields# 显示:标题、内容、摘要、分类、标签# 写文件---作者、时间不写# 重写 保存模型方法# 请求的用户 赋值给 对象作者
后台注册模型
class PostAdmin(admin.ModelAdmin): list_display = [‘title’,’created_time’,’modified_time’,’category’,’author’] fields = [‘title’,’body’,’summary’,’category’,’tags’]
def save_model(self, request, obj, form, change) -> None:obj.author = request.usersuper().save_model(request, obj, form, change)
admin.site.register(Post,PostAdmin) admin.site.register(Category) admin.site.register(Tag)
<a name="RDkIF"></a>#### 装饰器写法```pythonfrom django.contrib import adminfrom .models import Post,Category,Tag# 管理模型# 显示 列表的字段: 标题、时间、分类、作者# 修改显示:标题、内容、摘要、分类标签# 重写 保持模型方法# 请求的用户 赋值给 对象作者@admin.register(Post)class PostAdmin(admin.ModelAdmin):list_display = ['title','created_time','modified_time','category','author']fields = ['title','body','summary','category','tags']def save_model(self, request, obj, form, change) -> None:obj.author = request.userreturn super().save_model(request, obj, form, change)admin.site.register(Category)admin.site.register(Tag)
运行效果
运行 py manage.py runserver
打开地址:http://localhost:8000/admin/
管理员登录
下载静态资源
下载博客模板,https://github.com/jukanntenn/django-blog-tutorial-templates
- 下载下来是完整html,直接打开就 index.html 看完整代码
将js和css存放在项目下 static目录下 blog文件内3
模板
这模板内容,不是重点,复制粘贴过一下就行了
页面头尾是重复的,将他们提炼出来放在 base.html ```html <!DOCTYPE html>
<a name="umve2"></a>#### static的指定路径- 路径可能会跟着项目变而变,才用 static- 原本 :`href="css/custom.css"`- 改为:`href="{% static 'blog/css/custom.css' %}"`- 因为资源存放在 blog目录下- 外链接就不需要改- 加载 静态资源 ` {% load static %}`- 用宏,` {% block %} ` 是变得内容,用块来标识<a name="TZgEw"></a>#### base页面原本页面应该是这样- 第一个容器 container- header > row- 2个 col 是互不,加起来 = 12- 写的是 导航栏:- navbar-header、navbar-collapse- header-search-box- search-menu、seacrch-form- 接着写 container-body- `main `> article * 4 + pagination分页- `aside` > widget * 4 (recent-post、archives、category、tag-cloud)、rss- footer 页脚其中 页头、页脚是重复,换句话 main、aside内容一直变,将他们变成 块方便复用<br />他们结构没变,变的只是数据而已。<br />**base相对于父模板,内容不断嵌套在里面就行**```html{% load static %}<!DOCTYPE html><html><head><title>Black & White</title><!-- meta --><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1">{% static '' %}<!-- css --><link rel="stylesheet" href="{% static 'blog/css/bootstrap.min.css' %}"><link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"><link rel="stylesheet" href="{% static 'blog/css/pace.css' %}"><link rel="stylesheet" href="{% static 'css/custom.css' %}"><!-- js --><script src="{% static 'js/jquery-2.1.3.min.js' %}"></script><script src="{% static 'js/bootstrap.min.js' %}"></script><script src="{% static 'js/pace.min.js' %}"></script><script src="{% static 'js/modernizr.custom.js' %}"></script></head><body><div class="container"><header class="site-header"><div class="row"><div class="col-md-4 col-sm-5 col-xs-8"><div class="logo"><h1><a href=""> <b>Black</b> & white </a></h1></div></div><div class="col-md-8 col-sm-7 col-xs-4"><nav class="main-nav" role="navigation"><div class="navbar-header"><button class="navbar-toggle" id="trigger-overlay" type="button"><span class="ion-navicon"></span></button></div><div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"><ul class="nav navbar-nav navbar-right"><li class="cl-effect-11"><a href="" data-hover="首页">首页</a></li><li class="cl-effect-11"><a href="" data-hover="博客">博客</a></li><li class="cl-effect-11"><a href="" data-hover="关于">关于</a></li><li class="cl-effect-11"><a href="" data-hover="联系">联系</a></li></ul></div></nav><div id="header-search-box"><a href="" id="search-menu"> <span id="search-icon" class="ion-ios-search-strong"></span></a><div id="search-form" class="search-form"><form action="" method="get" id="searchform" role="search"><input type="search" placeholder="搜索" required><button type="submit"> <span class="ion-ios-search-strong"></span></button></form></div></div></div></div></header></div><div class="content-body"><div class="container"><div class="row"><main class="col-md-8">{% block main %}{% endblock main %}</main><aside class="col-md-4">{% block toc %}{% endblock toc %}</aside></div></div></div><script src="{% static 'js/script.js' %}"></script></body></html>
index页面
- extends,继承base页面。 公共部分内容显示
- block是块,个性化的内容,main是块名
{% block 名 %} {% endblock 名%}
- empty,块是为空的情况显示
html结构
- areicle
- entry-title
- entry-meta
- category、date、author、comments-link、views-count
- entry-content clearfix
- p 摘要内容
- read-more > a.more-link ```html {% extends ‘base.html ‘%}
{% block main %} {% for post in list_posts %}
{{ post.title }}
{{ post.summary }}
<a name="hla0Y"></a>#### detail 页面- 块 toc 存放的是侧边栏- aside 侧边栏就 5个部分- 最新文章、归档--时间日期、分类、标签云、RSS订阅```html{% extends 'base.html' %}{% block main %}<article class="post post-{{ post.pk }}"><header class="entry-header"><h1 class="entry-title">{{ post.title }}</h1><div class="entry-meta"><span class="post-category"><a href="#">{{ post.category.name }}</a></span><span class="post-date"><a href="#"><time class="entry-date"datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span><span class="post-author"><a href="#">{{ post.author }}</a></span><span class="comments-link"><a href="{{ post.get_absolute_url }}#comment-area">{{ post.comment_set.count }} 评论</a></span><span class="views-count"><a href="#">588 阅读</a></span></div></header><div class="entry-content clearfix">{{ post.body | safe }}</div></article>{% endblock main %}{% block toc %}<div class="widget widget-recent-posts"><h3 class="widget-title">最新文章</h3><ul><li><a href="#">Django 博客开发入门教程:前言</a></li><li><a href="#">Django 博客使用 Markdown 自动生成文章目录</a></li><li><a href="#">部署 Django 博客</a></li></ul></div><div class="widget widget-archives"><h3 class="widget-title">归档</h3><ul><li><a href="#">2017 年 5 月</a></li><li><a href="#">2017 年 4 月</a></li><li><a href="#">2017 年 3 月</a></li></ul></div><div class="widget widget-category"><h3 class="widget-title">分类</h3><ul><li><a href="#">Django 博客教程 <span class="post-count">(13)</span></a></li><li><a href="#">Python 教程 <span class="post-count">(11)</span></a></li><li><a href="#">Django 用户认证 <span class="post-count">(8)</span></a></li></ul></div><div class="widget widget-tag-cloud"><h3 class="widget-title">标签云</h3><ul><li><a href="#">Django</a></li><li><a href="#">Python</a></li><li><a href="#">Java</a></li><li><a href="#">笔记</a></li><li><a href="#">文档</a></li><li><a href="#">AngularJS</a></li><li><a href="#">CSS</a></li><li><a href="#">JavaScript</a></li><li><a href="#">Snippet</a></li><li><a href="#">jQuery</a></li></ul></div><div class="rss"><a href=""><span class="ion-social-rss-outline"></span> RSS 订阅</a></div>{% endblock toc %}
第三步
函数
index函数
- 给
index页面,给post_list变量 - post_list 是全部文章数据,要排序 ```python from django.shortcuts import render,get_object_or_404,redirect from django.urls import reverse
from .models import Post,Category,Tag
import markdown import re from django.utils.text import slugify from markdown.extensions.toc import TocExtension
index 函数
# 获取文章的全部数据,排序; 赋值给 post_list# 渲染到 index.html页面,给post_list
def index(request): post_list = Post.objects.all().order_by(‘-created_time’) return render(request,’blog/index.html’,{‘post_list’:post_list})
<a name="TOJJA"></a>#### archive函数- 给` index `页面,给` post_list `变量- post_list 是**过滤指定年月**的所有文章数据,要排序```python# archive 函数# 条件过滤获取文章的数据,按创建时间排序。__year __month# 赋值给 post_list# 渲染到 index.html页面,给post_listdef archive(request,year,month):post_list = Post.objects.filter(created_time__year = year,created_time__month = month,).order_by('-created_time')return render(request,'blog/index.html',{'post_list':post_list})
category、tag
- 给 index页面数据,post_list
- post_list 是 过滤指定分类的 ,排序
def category(request,pk): cate = get_object_or_404(Category,pk=pk) post_list = Post.objects.filter(category=cate).order_by(‘-created_time’) return render(request,’blog/index.html’,{‘post_list’:post_list})
tag 函数
# 与category类似# 获取指定标签,赋值给t# 在文章里过滤指定标签出来,赋值给post_list# 渲染到 index.html页面,给post_list
def tag(request,pk): t = get_object_or_404(Tag,pk=pk) post_list = Post.objects.filter(tags=t).order_by(‘-created_time’) return render(request,’blog/index.html’,{‘post_list’:post_list})
<a name="jfdbH"></a>#### detail 函数- 给 detail页面,数据 post- post,是 指定的 文章数据- 实例化,markdown,赋值给 md- 将 post.body 转为 html文本,赋值给 post.body- 正则指定内容,不存在就是空字符串。赋值 post.tocre.search- (要的模式,匹配范围,规则) 在字符串中匹配值- re.group() 返回键值对对象- re.S 任意字符 I忽略大小写 M对行模式正则- `. `,匹配 0或多次- `\s`, 出现空白就匹配- ` \S`, 出现非空白就匹配`markdown.Markdown().toc` 生成目录结构<br />模型类.objects.filter(属性名__运算符=值)<br /> 比较查询:gt、gte、lt、lte<br /> 日期查询:day、year、month、day、week_day、hour、minute、second```pythonfrom django.shortcuts import render,get_object_or_404,redirectfrom django.urls import reversefrom .models import Post,Category,Tagimport markdownimport refrom django.utils.text import slugifyfrom markdown.extensions.toc import TocExtension# detail函数# 获取指定文章数据,赋值给 post# 实例化markdown,赋值给 md# 扩展选项:常规的扩展、亮高、目录,能识别中文# extensions、extra、codehilite、TocExtension、slugify# 将文章内容 转为 html格式,赋值给 post.body# 正则搜索 符合条件的 包含 (.toc标签 的 ul) 赋值给m# 包含内容: <div class="toc"># <ul></ul># </div># 范围: md.toc# 规则: re.S 空白/换行符就匹配# 判断:最终赋值给 post.toc# 三元写法# 取键值对对象的第一个, re.group(1),m不是空的话,# 否则,为空字符串# 渲染到 detail.html页面,给postdef detail(request,pk):post = get_object_or_404(Post,pk=pk)md = markdown.Markdown(extensions=['markdown.extensions.extra','markdown.extensions.codehilite',TocExtension(slugify= slugify),])post.body = md.convert(post.body) #将md转为html, 也会生成目录 md.tocm = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>',md.toc,re.S)post.toc = m.group(1) if m is not None else ''return render(request,'blog/detail.html',{'post':post})
路由
from ctypes.wintypes import tagSIZEfrom django.urls import pathfrom . import views# 路由# /# post/数字,# archive/年/月# category/数字# tags/数字app_name = 'blog'urlpatterns = [path('',views.index,name='index'),path('posts/<int:pk>',views.detail,name='detail'),path('archive/<int:year>/<int:month>',views.archive,name='archive'),path('category/<int:pk>',views.category,name='category'),path('tags/<int:pk>',views.tag,name='tag'),]
运行效果

类视图函数
路由
comment应用
第一步:评论
配置,注册
INSTALLED_APPS = ['comments',]
模型
from django.db import modelsfrom django.utils import timezone# 评论模型# 字段:名字、邮箱、链接、内容、创建时间、关联博客的文章 blog.Post# Meta属性# __str__方法# 将self.name赋值给第一个{}, self.text赋值给第二个{},裁剪前20class 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',on_delete=models.CASCADE,verbose_name='文章') # 关联到Postclass Meta():verbose_name = '评论'verbose_name_plural = verbose_namedef __str__(self) -> str:return '{}:{}'.format(self.name,self.text[:20])
表单
新建表单
- 绑定 Comment模型
- 显示:名、邮箱、网站、内容 ```python from django import forms from .models import Comment
class CommentForm(forms.ModelForm):
class Meta():model = Commentfields = ['name','email','url','text']
<a name="KWxe7"></a>### 后台管理```pythonfrom django.contrib import adminfrom .models import Comment# 管理comment# 列表显示:名、邮箱、网站、文章、创建时间# 表单字段显示:名、邮箱、网站、内容、文章@admin.register(Comment)class CommentAdmin(admin.ModelAdmin):list_display = ['name','email','url','post','created_time']fields = ['name','email','url','text','post']
自定义 “组件”
在comments应用下,创建 templatetags文件
- 创建
__init__.py表示是一个包 comments_extras.py,这个名字随意,前面的是固定写法show_comment_form 函数
评论表单
给
inclusion/_form.html页面,上下文对象(内容 post,form)- 注册自定义组合
@template.Library().inclusion_tag()- 指定模板,函数处理后,数据连同一起给
- 注册自定义组合
- 模板怎么用
- 定义好 “组件”
- 在 detail页面 用
- 先加载 “组件”
{% load comments_extras %} - 调用
{% show_comment_form post %}post是父模板给的数据,- detial页面的数据 post_list > post ```python
- 先加载 “组件”
from django import template from ..form import CommentForm
实例化 模板自定义标签 Library,赋值给 register
通过装饰器,注册调用inclusion_tag方法
# 渲染_form页面,数据也能传递给父模板 take_context
函数, show_comment_form 展示评论表单
# 参数:上下文,post,空form# 判断form,如果为空# 实例化表单 CommentForm,赋值给 form# 返回上下文字典:form,post
register = 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}
<a name="COMgF"></a>#### show_comments 函数显示评论内容- 在 detail页面 用- 先加载 “组件”` {% load comments_extras %}`- 调用` {% show_comments post %}` post是传递参数,- detial页面的数据 post_list > post```python# 装饰器,注册自定义标签 @template.Library().inclusion_tag# 渲染模板 _list.html页面# 数据也传递给父模板 takes-context=true# 评论内容# 模型有Comment.post,反向查询 post.comment_set获取post对应 全部评论# 排序,赋值给 comment_list# 评论数, count()方法,赋值给 comment_count# 返回: 上下文对象(评论列表、总评论数)# 显示评论内容,show-comments@register.inclusion_tag('comments/inclusions/_list.html', takes_context=True)def show_comments(context, post):# post.comment_set.all() 也等价于 Comment.objects.filter(post=post)# Post.objects.filter(category=cate) 也可以等价写为 cate.post_set.all()。comment_list = post.comment_set.all().order_by('-created_time') #获取post对应全部评论comment_count = comment_list.count()return {'comment_count': comment_count,'comment_list': comment_list,
模板
在tmeplates下创建 comments文件夹
- 创建 prieview.html文件
- 创建文件夹 inclusions
- 创建 form、list
_form 页面
效果是这样的
<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>
_list 页面
下个如下
<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>
preview 页面
- 提交表单包含错误,渲染 该页面 ```python {% extends ‘base.html’ %} {% load comments_extras %}
{% block main %} {% show_comment_form post form %} {% endblock main%}
<a name="qM8Yw"></a>### 函数<a name="Wu4Oe"></a>#### comment.- 添加表单数据, 保存到数据库- 关联被评论的文章- 渲染到` preview`页面,参数 form,post```pythonfrom django.shortcuts import render,get_object_or_404,redirectfrom .form import CommentFormfrom blog.models import Postfrom django.views.decorators.http import require_POSTfrom django.contrib import messages# Create your views here.# post请求装饰器# comment函数# 获取指定文章数据,赋值给 post# 实例化评论表单,将POST请求数据放进去,赋值给 form# 检查form# commit=False 创建模型不给数据库,赋值给 comment# 将 post 赋值给 comment.post, 被评论的文章关联起来# 保存 comment.save# 重定向到 post模型(detail页面)# 渲染 preview页面# 实例化message对象,# 扩展提示 extra-tags# 将对象,post,form赋值给 context# 渲染 preview.html页面@require_POSTdef comment(request,post_pk):post = get_object_or_404(Post,pk=post_pk)form = CommentForm(request.POST)if form.is_valid():comment = form.save(commit=False)comment.post = postcomment.save()# 参数:消息级别,额外标签messages.add_message(request,messages.SUCCESS,'评论发布成功',extra_tags='success')return redirect(post) # 重定向到模型实例, 调用模型里的 get_absolute_urlcontext = {'post':post,'form':form,}# messages.add_message(request,messages.ERROR,'评论失败,重新提交',extra_tags='danger')return render(request,'comments/preview.html',context=context)
messages 对象
在base页面上显示
<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">×</span></button>{{ message }}</div>{% endfor %}{% endif %}
路由
主路由
from django.contrib import adminfrom django.urls import path,includeurlpatterns = [path('admin/', admin.site.urls),path('',include('blog.urls')),path('',include('comments.urls')), # 新增这一行]
子路由
在comments应用下,创建urls.py文件.
from django.urls import pathfrom . import viewsapp_name = 'comments'urlpatterns = [path('comment/<int:post_pk>',views.comment,name='comment'),]
