- 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 Thursday
cd Thursday
django-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写函数处理
```python
from django.shortcuts import render
def 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 文件
```python
from django.urls import path
from . import views
app_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_name
def __str__(self) -> str:
return self.name
class Tag(models.Model): name = models.CharField(max_length=100)
class Meta():
verbose_name = '标签'
verbose_name_plural = verbose_name
def __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_name
def __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的标签除去,留下文本内容
```python
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils.html import strip_tags
import 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): # absolute
return 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.user
super().save_model(request, obj, form, change)
admin.site.register(Post,PostAdmin) admin.site.register(Category) admin.site.register(Tag)
<a name="RDkIF"></a>
#### 装饰器写法
```python
from django.contrib import admin
from .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.user
return 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_list
def 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.toc
re.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
```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
# 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页面,给post
def 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.toc
m = 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 tagSIZE
from django.urls import path
from . 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 models
from django.utils import timezone
# 评论模型
# 字段:名字、邮箱、链接、内容、创建时间、关联博客的文章 blog.Post
# Meta属性
# __str__方法
# 将self.name赋值给第一个{}, self.text赋值给第二个{},裁剪前20
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',on_delete=models.CASCADE,verbose_name='文章') # 关联到Post
class Meta():
verbose_name = '评论'
verbose_name_plural = verbose_name
def __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 = Comment
fields = ['name','email','url','text']
<a name="KWxe7"></a>
### 后台管理
```python
from django.contrib import admin
from .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
```python
from django.shortcuts import render,get_object_or_404,redirect
from .form import CommentForm
from blog.models import Post
from django.views.decorators.http import require_POST
from 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_POST
def 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 = post
comment.save()
# 参数:消息级别,额外标签
messages.add_message(request,messages.SUCCESS,'评论发布成功',extra_tags='success')
return redirect(post) # 重定向到模型实例, 调用模型里的 get_absolute_url
context = {
'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"><span
aria-hidden="true">×</span></button>
{{ message }}
</div>
{% endfor %}
{% endif %}
路由
主路由
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应用下,创建urls.py
文件.
from django.urls import path
from . import views
app_name = 'comments'
urlpatterns = [
path('comment/<int:post_pk>',views.comment,name='comment'),
]