这是用Django写的一个小型的博客,重新学习下Django也给自己的学习留下点记忆吧。写的不是很好,但是都是我一字一句的手打出来了,希望大牛看见我的文章,轻喷,感谢。
系统 Windows 10
python == 3.6.4
django == 2.2.3
Markdown == 3.1.1
后续用到什么我在补充
进入安装好Django的虚拟环境,并且进到项目存放目录中执行:
django-admin startproject Django_blog
目录结构如下:
修改本地时间及语言
在setting.py文件中找到以下两行代码进行修改
"""Django_blog/Django_blog/setting.py"""
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
# 把英文改为中文
LANGUAGE_CODE = 'zh-hans'
# 把国际时区改为中国时区(东八区)
TIME_ZONE = 'Asia/Shanghai'
我用的是pycharm直接运行就可以。或者直接进到项目目录执行:
python manage.py runserver
进入到项目所在目录执行
python manage.py startapp blog
并且注册app
在setting.py文件的INSTALLED_APPS增加blog
"""Django_blog/Django_blog/setting.py"""
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # 注册 blog 应用
]
因为上一周事情比较多,博客就没有更新,现在继续更新,
设计思路:
分为了三张表,
博客分类:
ID | 分类名 |
---|---|
1 | Django |
2 | Python |
博客标签
ID | 标签名 |
---|---|
1 | Django学习 |
2 | Python学习 |
博客文章:
文章ID | 标题 | 正文 | 发表时间 | 分类 | 标签 |
---|---|---|---|---|---|
1 | 标题1 | 正文1 | 2019-11-10 | Django | Django学习 |
2 | 标题2 | 正文2 | 2019-11-11 | Django | Django学习 |
3 | 标题3 | 正文3 | 2019-11-11 | Python | Django学习 |
Django中比较方便的就是已经把数据库的语法,已经转换成Python语法,直接写Python代码就可以了,这是Django相当方便的地方:
# blog/models.py
from django.db import models
# Django后台管理系统的用户,不是自己定义的
from django.contrib.auth.models import User
# 博客分类
class Category(models.Model):
name = models.CharField(max_length=100, verbose_name='分类名')
# 博客标签
class Tag(models.Model):
name = models.CharField(max_length=100, verbose_name='标签名')
# 博客文章
class Post(models.Model):
# 文章标题
title = models.CharField(max_length=70, verbose_name='文章标题')
# 正文
body = models.TextField(verbose_name='文章正文')
# 创建时间和修改时间
created_time = models.DateTimeField(verbose_name='创建时间')
modified_time = models.DateTimeField(verbose_name='修改时间')
# 文章摘要 blank=True 允许为空
excerpt = models.CharField(max_length=200, blank=True)
# 分类 on_delete=models.CASCADE 参数是关联删除 一对多
category = models.ForeignKey(Category, on_delete=models.CASCADE)
# 标签 多对多,一个文章可以有多个标签,
tags = models.ManyToManyField(Tag, blank=True)
# 作者 一对多,一个作者可以写很多文章,
author = models.ForeignKey(User, on_delete=models.CASCADE)
相关链接
Django模型内置字段类型
Django,一对多,多对多官方文档
在虚拟环境下执行以下两条命令,完成数据库迁移。
python manage.py makemigrations
python manage.py migrate
看见以下图片证明迁移成功,django 在 blog 应用的 migrations 目录下生成了一个 0001_initial.py 文件,这个文件是 django 用来记录我们对模型做了哪些修改的文件。
以下语句,可以查看得刚才在迁移数据库时Django做了什么。
python manage.py sqlmigrate blog 0001
因为是自己搭建的一个小型的博客,所以这里的数据库使用SQLite3
如何想改成MySQL也是可以的。
blogproject/settings.py
DATABASES = {
# sqlite3配置
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# mysql配置
# 'default': {
# 'ENGINE': 'django.db.backends.mysql',
# 'NAME': 'mysql_name',
# 'USER': 'mysql_user',
# 'PASSWORD': 'mysql_password',
# 'HOST': 'mysql_host',
# 'PORT': 'mysql_port',
}
}
在manage.py所在目录下执行
python manage.py shell
>>> from blog.models import Category, Tag, Post
>>> c = Category(name='category test')
>>> c.save()
>>> t = Tag(name='tag test')
>>> t.save()
我们首先导入 3 个之前写好的模型类,然后实例化了一个 Category 类和一个 Tag 类,为他们的属性 name 赋了值。为了让 django 把这些数据保存进数据库,调用实例的 save 方法即可。
标签,分类都创建好了,还需要创建用户
输入quit()退出shell模式
输入命令创建用户
python manage.py createsuperuser
看见以下效果代表创建成功。注意输入密码的时候是什么都不会显示的,直接输入就可以。
接下来就是创建文章了。
再次进入shell
python manage.py shell
>>> from blog.models import Category, Tag, Post
>>> from django.utils import timezone
>>> from django.contrib.auth.models import User
>>> user = User.objects.get(username='xxxx') # 你刚才创建的名字
>>> c = Category.objects.get(name='category test')
>>> p = Post(title='title test', body='body test', created_time=timezone.now(), modified_time=timezone.now(), category=c, author=user)
>>> p.save()
由于我们重启了 shell,因此需要重新导入了 Category、Tag、Post 以及 User。我们还导入了一个 django 提供的辅助模块 timezone,timezone模块相比于自带的datetime,多了对时区的处理。
还是在shell里面输入
>>> Category.objects.all()
>>> Tag.objects.all()
>>> Post.objects.all()
现在可读性很差,需要做的是在模型的类里定义__str__方法
blog/models.py
class Category(models.Model):
...
def __str__(self):
return self.name
class Tag(models.Model):
...
def __str__(self):
return self.name
class Post(models.Model):
...
def __str__(self):
return self.title
写好之后退出shell再次进入shell
python manage.py shell
>>> from blog.models import Category, Tag, Post
>>> Category.objects.all()
>>> Tag.objects.all()
>>> Post.objects.all()
>>> Post.objects.get(title='title test')
还是在shell里操作,不用退出shell,如果退出再重新导入下Category, Tag, Post,
使用get方法取到名为category test的分类,然后进行更改,更改之后使用save()保存,其他的也是一样修改
>>> c = Category.objects.get(name='category test')
>>> c.name = 'category test new'
>>> c.save()
>>> Category.objects.all()
还是在shell里操作,不用退出shell,如果退出再重新导入下Category, Tag, Post,
p = Post.objects.get(title='title test')
p.delete()
Post.objects.all()
先根据标题 title 的值从数据库中取出 Post,保存在变量 p 中,然后调用它的delete 方法,最后看到 Post.objects.all() 返回了一个空的 QuerySet(类似于一个列表),表明数据库中已经没有 Post,Post 已经被删除了。
Django增删查改官方文档
今天就更新到这里吧,2019/11/11,要去剁手了。
首先在blog下面的新建一个urls.py文件,新建之后的目录,目录结构如下:
# blog\urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
"""第一个('')是网址,第二个(views.index)是处理函数,第三个是属于别名"""
接下来写视图函数
# blog\views.py
from django.http import HttpResponse
def index(request):
return HttpResponse("欢迎访问我的博客首页!")
最后一步了,自己在app里写url了也写views了,但是还没在Django里配置,
在项目的目录的urls.py文件中配置,(也就是和settings.py)平级的urls.py里配置
# Django_blog/urls.py
"""这里面之前是有代码的,需要导入include然后在进行配置""""
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# 配置blog路由
path('', include('blog.urls')),
]
配置完直接运行Django项目,在访问就会在浏览器中看到
“欢迎来到我的博客首页”
如果看到这几个字,恭喜你,可以进行下一节的学习了。
在项目的根目录下新建一个名为“templates”,然后在templates下新建一个名为“blog”的文件夹,用来存放blog的模板,然后在blog文件夹下新建一个index.html
目录结构如下:
然后在index.html写下如下代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
<h1>{{ welcome }}</h1>
</body>
</html>
这里面用到了两处模板语法{{ title }}和{{ welcome }}
这两个值是可以直接在后端取得。
接下来是配置模板路径
在 settings.py 文件里找到TEMPLATES
# Django_blog/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# 这是初始的内容
# 'DIRS': [],
# 配置模板
'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',
],
},
},
]
第一个页面的最后一步在把视图函数改一下,因为之前的返回方式是没有使用到模板的,更改完使用模板的。
# blog/views.py
def index(request):
# return HttpResponse('欢迎来到我的博客首页')
# title和welcome直接传到index.html上直接使用
context = {'title':'博客首页', 'welcome':'欢迎来到我的博客首页'}
return render(request, 'blog/index.html', context)
这就是第一个页面。
2019/11/13 隔了两天继续更新的,我不想把这一篇文章分为几个小的文章,我这也是刚开始学习写博客,写的不好的大牛多批评指教。
首先是需要下模板,模板地址:
Django模板下载地址
先在 blog 应用下建立一个 static 文件夹,再在 static 目录下建立一个 blog 文件夹,这是为了别的应用里也有同名的css和js文件。在把下载css和js文件夹和文件夹下面的文件一起复制到 \static\blog\里面
目录结构如下:
在把模板里的index.html替换掉你之前的index.html文件
现在启动不行的,需要把css和js的引用改一下
首先在第一行导入:{% load static %}
找到index.html文件的 “head” 标签
# templates/blog/index.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">
<!-- css -->
- <link rel="stylesheet" href="css/bootstrap.min.css">
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
- <link rel="stylesheet" href="css/pace.css">
- <link rel="stylesheet" href="css/custom.css">
+ <link rel="stylesheet" href="{% static 'blog/css/bootstrap.min.css' %}">
+ <link rel="stylesheet" href="{% static 'blog/css/pace.css' %}">
+ <link rel="stylesheet" href="{% static 'blog/css/custom.css' %}">
<!-- js -->
- <script src="js/jquery-2.1.3.min.js"></script>
- <script src="js/bootstrap.min.js"></script>
- <script src="js/pace.min.js"></script>
- <script src="js/modernizr.custom.js"></script>
+ <script src="{% static 'blog/js/jquery-2.1.3.min.js' %}"></script>
+ <script src="{% static 'blog/js/bootstrap.min.js' %}"></script>
+ <script src="{% static 'blog/js/pace.min.js' %}"></script>
+ <script src="{% static 'blog/js/modernizr.custom.js' %}"></script>
</head>
<body>
<!-- 其它内容 -->
- <script src="js/script.js' %}"></script>
+ <script src="{% static 'blog/js/script.js' %}"></script>
</body>
</html>
带“+”的是修改后的,带“-”的是修改之前的,改完之后把带“-”的删除就行
现在也别着急,接下来使用模板语言渲染页面,
# blog\views.py
# 导入模型中的Post使用object查询
from .models import Post
def index(request):
# 使用模板操作数据库,取到所有数据,然后使用order_by按插入时间排序。'后插入的显示在前面'
post_list = Post.objects.all().order_by('-created_time')
return render(request, 'blog/index.html', context={'post_list': post_list})
在index.html文件中找 “article” 标签,应该有四五个,留下一个就可以了。
修改如下:
# templates\blog\index.html
...
{% for post in post_list %}
<article class="post post-{{ post.pk }}">
...
</article>
{% empty %}
<div class="no-post">暂时还没有发布的文章!</div>
{% endfor %}
...
# 还有替换的地方
...
<h1 class="entry-title">
<a href="single.html">{{ post.title }}</a>
</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="#">4 评论</a></span>
<span class="views-count"><a href="#">588 阅读</a></span>
</div>
...
···
<div class="entry-content clearfix">
<p>{{ post.excerpt }}</p>
<div class="read-more cl-effect-14">
<a href="#" class="more-link">继续阅读 <span class="meta-nav">→</span></a>
</div>
</div>
···
这里有两个参数需要说一下
{% for post in post_list %}
和python中for循环很像,但是有{% %}包着,必须以{% endfor %}结尾,要不然会报错
{% empty %} 即数据库里没有文章时显示{% empty %} 下面的内容
替换完这些就可以运行项目了。
截止目前完整的index.html如下:
# templates\blog\index.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">
<!-- css -->
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link rel="stylesheet" href="{% static 'blog/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'blog/css/pace.css' %}">
<link rel="stylesheet" href="{% static 'blog/css/custom.css' %}">
<!-- js -->
<script src="{% static 'blog/js/jquery-2.1.3.min.js' %}"></script>
<script src="{% static 'blog/js/bootstrap.min.js' %}"></script>
<script src="{% static 'blog/js/pace.min.js' %}"></script>
<script src="{% static 'blog/js/modernizr.custom.js' %}"></script>
</head>
<body>
<div class="container">
<header id="site-header">
<div class="row">
<div class="col-md-4 col-sm-5 col-xs-8">
<div class="logo">
<h1><a href="index.html"><b>Black</b> & White</a></h1>
</div>
</div><!-- col-md-4 -->
<div class="col-md-8 col-sm-7 col-xs-4">
<nav class="main-nav" role="navigation">
<div class="navbar-header">
<button type="button" id="trigger-overlay" class="navbar-toggle">
<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="index.html" data-hover="首页">首页</a></li>
<li class="cl-effect-11"><a href="full-width.html" data-hover="博客">博客</a></li>
<li class="cl-effect-11"><a href="about.html" data-hover="关于">关于</a></li>
<li class="cl-effect-11"><a href="contact.html" data-hover="联系">联系</a></li>
</ul>
</div><!-- /.navbar-collapse -->
</nav>
<div id="header-search-box">
<a id="search-menu" href="#"><span id="search-icon" class="ion-ios-search-strong"></span></a>
<div id="search-form" class="search-form">
<form role="search" method="get" id="searchform" action="#">
<input type="search" placeholder="搜索" required>
<button type="submit"><span class="ion-ios-search-strong"></span></button>
</form>
</div>
</div>
</div><!-- col-md-8 -->
</div>
</header>
</div>
<div class="copyrights">Collect from <a href="http://www.cssmoban.com/">网页模板</a></div>
<div class="copyrights">Modified by <a href="http://zmrenwu.com/">yangzw的博客</a></div>
<div class="content-body">
<div class="container">
<div class="row">
<main class="col-md-8">
{% for post in post_list %}
<article class="post post-{{ post.pk }}">
<header class="entry-header">
<h1 class="entry-title">
<a href="single.html">{{ post.title }}</a>
</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="#">4 评论</a></span>
<span class="views-count"><a href="#">588 阅读</a></span>
</div>
</header>
<div class="entry-content clearfix">
<p>{{ post.excerpt }}</p>
<div class="read-more cl-effect-14">
<a href="#" class="more-link">继续阅读 <span class="meta-nav">→</span></a>
</div>
</div>
</article>
{% empty %}
<div class="no-post">暂时还没有发布的文章!</div>
{% endfor %}
<!-- 简单分页效果
<div class="pagination-simple">
<a href="#">上一页</a>
<span class="current">第 6 页 / 共 11 页</span>
<a href="#">下一页</a>
</div>
-->
<div class="pagination">
<ul>
<li><a href="">1</a></li>
<li><a href="">...</a></li>
<li><a href="">4</a></li>
<li><a href="">5</a></li>
<li class="current"><a href="">6</a></li>
<li><a href="">7</a></li>
<li><a href="">8</a></li>
<li><a href="">...</a></li>
<li><a href="">11</a></li>
</ul>
</div>
</main>
<aside class="col-md-4">
<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>
</aside>
</div>
</div>
</div>
<footer id="site-footer">
<div class="container">
<div class="row">
<div class="col-md-12">
<p class="copyright">© 2017 - 采集自<a href="http://www.cssmoban.com/"
target="_blank" title="模板之家">模板之家</a>
- 由<a href="http://zmrenwu.com/" title="网页模板" target="_blank">yangzw的博客</a>修改
</p>
</div>
</div>
</div>
</footer>
<!-- Mobile Menu -->
<div class="overlay overlay-hugeinc">
<button type="button" class="overlay-close"><span class="ion-ios-close-empty"></span></button>
<nav>
<ul>
<li><a href="index.html">首页</a></li>
<li><a href="full-width.html">博客</a></li>
<li><a href="about.html">关于</a></li>
<li><a href="contact.html">联系</a></li>
</ul>
</nav>
</div>
<script src="{% static 'blog/js/script.js' %}"></script>
</body>
</html>
启动项目之后看见如下图片:
恭喜你可以进行下一节的学习了。
因为现在后台数据库里还没有数据,所以不会显示。准备下一节插入数据:
2019/11/14
在blog下的admin.py注测后台模型
# blog\admin.py
from django.contrib import admin
from .models import Post, Category, Tag
admin.site.register(Post)
admin.site.register(Category)
admin.site.register(Tag)
注册完之后启动项目访问 http://127.0.0.1:8000/admin/ 用你之前创建的用户登录就行
如果忘记之前创建的用户了。在虚拟环境中执行以下命令根据提示重新创建超级用户
python manage.py createsuperuser
我这是修改中文之后样子,你们不是也没关系。接下来进行设置。
blog文件夹下的apps.py
# blog\apps.py
class BlogConfig(AppConfig):
name = 'blog'
verbose_name = '博客'
然后在settings.py里进行注册,把之前的删除或者注释,要不然会报错。
# settings.py
NSTALLED_APPS = [
'django.contrib.admin',
...
'blog.apps.BlogConfig', # 注册 blog 应用
]
接下来是为模型model显示中文,只需在每个模型类下面添加一个Meta:
# blog\models.py
class Post(models.Model):
...
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
verbose_name = '文章'
verbose_name_plural = verbose_name
我只举这一个例子,这三个模型类都是一样。
并且在模型类的每个字段里都写上verbose_name字段
# blog\models.py
class Post(models.Model):
# 文章标题
title = models.CharField(max_length=70, verbose_name='文章标题')
# 正文
body = models.TextField(verbose_name='文章正文')
···
现在基本上都是中文了,但是在后台文章列表中显示的信息太少了。
在blog下的admin.py新写一个类,并注册
# blog\admin.py
class PostAdmin(admin.ModelAdmin):
# 控制 Post 后台列表页展示的字段
list_display = ['title', 'created_time', 'modified_time', 'category', 'author']
admin.site.register(Post, PostAdmin)
···
控制表单字段还是在admin.py里面
自动填充作者信息
# blog\admin.py
class PostAdmin(admin.ModelAdmin):
# 控制 Post 后台列表页展示的字段
list_display = ['title', 'created_time', 'modified_time', 'category', 'author']
# 控制后台表单展现的字段
fields = ['title', 'body', 'excerpt', 'category', 'tags']
# 自动填充作者信息
def save_model(self, request, obj, form, change):
obj.author = request.user
super().save_model(request, obj, form, change)
···
自动填充默认时间
需要修改的是blog下的models.py需要添加一个字段和导入timezone模块
timezone可以自己处理时区,但是python自带的dete不可以。
# blog\models.py
from django.utils import timezone
class Post(models.Model):
...
created_time = models.DateTimeField(verbose_name='创建时间', default=timezone.now)
...
最后修改时间和创建时间是不同的。不能用default,咱们可以对Post类的save方法进行重写。
# blog\models.py
from django.utils import timezone
class Post(models.Model):
...
def save(self, *args, **kwargs):
self.modified_time = timezone.now()
super().save(*args, **kwargs)
完成这一步就可以看看效果了。后台效果和你也可以添加文章试一下。
现在的夜里真冷了,赶紧洗澡睡觉了。
2019/11/15
在blog下的urls.py配置
# blog\urls.py
blog/urls.py
from django.urls import path
# 这行代码一定要写,要不然Django找不到是哪个app下的路由
app_name = 'blog'
urlpatterns = [
path('', views.index, name='index'),
path('posts//' , views.detail, name='detail'),
]
那个"int:pk"我会在后面模型里备注解释
在blog\models.py下修改代码
# blog\models.py
blog/models.py
···
from django.urls import reverse
···
class Post(models.Model):
...
# 管理url记得导入serverse 第一个参数告诉Django找到blog下detail
# 第二个参数把路由里的 替换为pk,例如/posts/2/
def get_absolute_url(self):
return reverse('blog:detail', kwargs={'pk': self.pk})
···
因为在路由写下detail视图函数了但是我们还没有。
在blog下的views.py修改
# blog\views.py
from django.shortcuts import render, get_object_or_404
···
def index(request):
···
# 博客详情页,导入系统的404页面,如果文章存在就显示,不存在就返回404页面
def detail(request, pk):
post = get_object_or_404(Post, pk=pk)
return render(request, 'blog/detail.html', context={'post': post})
那么问题来了,视图函数也有了但是页面好像还没有没关系,我们把木下载的前端模板中的single.html拷贝到template\blog和index.html平级,然后改名为detail.html
在index.html上有两个显示详情页的a标签需要改下路由,才能进到详情页。
以下是需要更改的两个地方
# blog\index.html
<h1 class="entry-title">
{#{{ post.title }}#}
<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</h1>
{#继续阅读 #}
<a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav">→</span></a>
考下来的每个页面都有很多相同的地方。所以我们可以把相同的地方提取出来做成一个模板,在改不同的地方
在templates下新建一个base.html注意是和templates下的blog文件夹是平级的
然后在把index.html文件中的所有文件拷到base.html中
然后找到main标签替换成以下内容
# templates\base.html
...
<main class="col-md-8">
{% block main %}
{% endblock main %}
</main>
<aside class="col-md-4">
{% block toc %}
{% endblock toc %}
...
</aside>
...
templates/blog/index.html
{% extends 'base.html' %}
{% block main %}
{% for post in post_list %}
<article class="post post-1">
...
</article>
{% empty %}
<div class="no-post">暂时还没有发布的文章!</div>
{% endfor %}
<!-- 简单分页效果
<div class="pagination-simple">
<a href="#">上一页</a>
<span class="current">第 6 页 / 共 11 页</span>
<a href="#">下一页</a>
</div>
-->
<div class="pagination">
...
</div>
{% endblock main %}
{% extends ‘base.html’ %}代表继承base.html页面
{% block main %}
{% endblock main %}
{% block toc %}
{% endblock toc %}
这两个里面是可以变化的东西,属于定制化的,他们之外的都是不变的可以被继承。
详情页也是继承base.html
# templates\blog\detail.html
{% extends 'base.html' %}
{% block main %}
<article class="post post-1">
...
</article>
<section class="comment-area">
...
</section>
{% endblock main %}
{% block toc %}
<div class="widget widget-content">
<h3 class="widget-title">文章目录</h3>
<ul>
<li>
<a href="#">教程特点</a>
</li>
<li>
<a href="#">谁适合这个教程</a>
</li>
<li>
<a href="#">在线预览</a>
</li>
<li>
<a href="#">资源列表</a>
</li>
<li>
<a href="#">获取帮助</a>
</li>
</ul>
</div>
{% endblock toc %}
这里面还是一些假数据呢,然后我们替换掉一部分内容。换成后台取出来的数据
# templates\blog\detail.html
<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="#">4 评论</a></span>
<span class="views-count"><a href="#">588 阅读</a></span>
</div>
</header>
<div class="entry-content clearfix">
{{ post.body }}
</div>
</article>
在运行就是一下效果了,因为我删除了一篇文章,所以,我的路由是这个样子。
屏幕分辨率有点。。。哈哈,我该换电脑了。
在虚拟环境执行
pip install markdown
在blog\views.py中,解析Markdown文章
# blog\views.py
···
import markdown
···
def detail(request, pk):
post = get_object_or_404(Post, pk=pk)
post.body = markdown.markdown(post.body,
extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc',
])
return render(request, 'blog/detail.html', context={'post': post})
下面是两篇可以简单学习Markdown语法的文章质量挺好的。
Markdown——入门指南
Markdown 语法说明 (简体中文版)
然后在admin后天管理系统添加带有Markdown语法的文章
因为语法的限制,那个Markdown代码那块需要自己更改下,标题和标签自己随意填写
# 一级标题
## 二级标题
### 三级标题
- 列表项1
- 列表项2
- 列表项3
> 这是一段引用
(```python)
给小括号去掉,然后在我写文字的部分拷贝一个函数进来。
(```)
现在看详情还不行呢,因为我们还需要改一个地方,取消Django默认转义。
在detail.html文件下找到{{ post.body }}
标签,该问{{ post.body | safe }}
这下就只剩代码高亮了,之前Markdown导入过代码高亮,但是样式很丑,
所以需要导入前端插件
在haed头里面增加以下信息
# base.html
<head>
...
<link href="https://cdn.bootcss.com/highlight.js/9.15.8/styles/github.min.css" rel="stylesheet">
<style>
.codehilite {
padding: 0;
}
/* for block of numbers */
.hljs-ln-numbers {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
text-align: center;
color: #ccc;
border-right: 1px solid #CCC;
vertical-align: top;
padding-right: 5px;
}
.hljs-ln-n {
width: 30px;
}
/* for block of code */
.hljs-ln .hljs-ln-code {
padding-left: 10px;
white-space: pre;
}
</style>
</head>
在body标签里添加以下信息
# base.html
<body>
···
<script src="https://cdn.bootcss.com/highlight.js/9.15.8/highlight.min.js"></script>
<script src="https://cdn.bootcss.com/highlightjs-line-numbers.js/2.7.0/highlightjs-line-numbers.min.js"></script>
<script>
hljs.initHighlightingOnLoad();
hljs.initLineNumbersOnLoad();
</script>
···
</body>
再次打开就可以看见很美观的代码高亮了。
今天北京的天很冷,出去吃顿饭冻死我了都,但是代码写的我很开心。2019/11/16
前一章我们已经把自动生成的模块导入到views.py里面了,直接在管理后台写一篇Markdown语法的博客文章。
我的文章内容如下:
@[toc]
# 主题一
标题一下面的正文Markdown
## 1-1
二标题内容1
### 1-1-1
三标题内容1
### 1-1-2
三标题内容2
## 1-2
二标题内容2
## 1-3
二标题内容3
### 这是标题三
标题三下的文字
首先需要把博客详情页的视图函数更改下。blog\views.py
# blog\views.py
import re
···
def detail(request, pk):
post = get_object_or_404(Post, pk=pk)
md = markdown.Markdown(extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
'markdown.extensions.toc',
])
post.body = md.convert(post.body)
# 判断目录是否为空
m = re.search(r'\s*(.*)
\s*', md.toc, re.S)
post.toc = m.group(1) if m is not None else ''
return render(request, 'blog/detail.html', context={'post': post})
然后更好下html文件,templates\blog\detail.html
这里的模板语法和safe就不解释了,如果不明白去看上面的safe函数
# templates\blog\detail.html
···
{% block toc %}
{% if post.toc %}
<div class="widget widget-content">
<h3 class="widget-title">文章目录</h3>
<div class="toc">
<ul>
{{ post.toc | safe }}
</ul>
</div>
</div>
{% endif %}
{% endblock toc %}
···
效果如下,侧边也有了,文章里也有了,我点击的是主题一,路由显示的不是很友好。
在详情页的是的视图函数里增加两个引用就行。\blog\views.py
记得引用要加上
# blog\views.py
···
from django.utils.text import slugify
from markdown.extensions.toc import TocExtension
def detail(request, pk):
···
md = markdown.Markdown(extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
# 'markdown.extensions.toc',
# 美化锚点
TocExtension(slugify=slugify),
])
···
return render(request, 'blog/detail.html', context={'post': post})
来看下效果,
好看多了,今天就这样了不知不觉的又到了后半夜。
2019/11/18
重写save函数之前自动生成创建时间咱们已经重写过。
在正文Post模型中,body字段里面里面取指定多少个字符保存到excerpt字段里面,记得导入Markdown和strip_tags
# blog\models.py
···
import markdown
from django.utils.html import strip_tags
···
class Post(models.Model):
···
def save(self, *args, **kwargs):
self.modified_time = timezone.now()
# 首先实例化一个 Markdown 类,用于渲染 body 的文本。
# 由于摘要并不需要生成文章目录,所以去掉了目录拓展。
md = markdown.Markdown(extensions=[
'markdown.extensions.extra',
'markdown.extensions.codehilite',
])
# 先将 Markdown 文本渲染成 HTML 文本
# 在进行判断文章是否输入摘要
# strip_tags 去掉 HTML 文本的全部 HTML 标签
# 从文本摘取前 54 个字符赋给 excerpt
if self.excerpt == None:
self.excerpt = strip_tags(md.convert(self.body))[:54]
super().save(*args, **kwargs)
然后index.html页面展示出来。
# templates\blog\index.html
<article class="post post-{{ post.pk }}">
...
<div class="entry-content clearfix">
<p>{{ post.excerpt }}...</p>
<div class="read-more cl-effect-14">
<a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav">→</span></a>
</div>
</div>
</article>
接下来划重点,想要文章有摘要,一定要新添加一篇文章,随便写些文本上去测试下就可以。我的结果是这样的。
使用 truncatechars 模板过滤器
这里的models.py不需要再改了,不需要再复写了。
直接用truncatechars
模板过滤器
# templates\blog\index.html
<article class="post post-{{ post.pk }}">
...
<div class="entry-content clearfix">
<p>{{ post.body|truncatechars:54 }}...</p>
<div class="read-more cl-effect-14">
<a href="{{ post.get_absolute_url }}" class="more-link">继续阅读 <span class="meta-nav">→</span></a>
</div>
</div>
</article>
纯文本文章可以,但是如果遇到了Markdown标题之列的会很难看,如下图
今天好像高烧了,要不然多更新点了,但是好难受啊,睡觉了。
2019/11/19
内置的模板标签比如{% static %}
或者{% for %} {% endfor%}
我们要写的是自定义模板标签,也可以说是定制标签,
下图圈出来的是写死的,今天要做的就是替换下图圈出的固定代码
首先需要在blog下创建templatetags的一个python文件夹,
然后在文件夹下创建一个__init__.py文件,让这个文件夹变成包,init.py里面不需要写东西。写上这个就变成python的包了。
并且在这个文件夹下创建blog_extras.py文件用于编写模板标签逻辑
目录结构如下
在blog_extras.py文件下写入以下代码
首先引入Django的template,然后实例化template.Library类,
并且用装饰器register.inclusion_tag装饰
show_recent_posts函数,
# blog\templatetags\blog_extras.py
from django import template
from ..models import Post, Category, Tag
register = template.Library()
# 最新文章模板标签
'''
takes_context 设置为 True 时将告诉 django,
在渲染 _recent_posts.html 模板时,
不仅传入show_recent_posts 返回的模板变量,
同时会传入父模板(即使用 {% show_recent_posts %} 模板标签的模板)
上下文(可以简单理解为渲染父模板的视图函数传入父模板的模板变量以及 django 自己传入的模板变量)。
当然这里并没有用到这个上下文,这里只是做个简单演示,如果需要用到,就可以在模板标签函数的定义中使用
context 变量引用这个上下文。
'''
@register.inclusion_tag('blog/inclusions/_recent_posts.html', takes_context=True)
def show_recent_posts(context, num=5):
return {'recent_post_list': Post.objects.all().order_by('-created_time')[:num],}
如果用pycharm写的话那个路径是报错的,这下就该写html页面了,
在templates\blog下创建inclusions文件夹,用于存放标模板标签的html在inclusions文件夹下创建 _recent_posts.html 文件
# templates\blog\inclusions\_recent_posts.html
<div class="widget widget-recent-posts">
<h3 class="widget-title">最新文章</h3>
<ul>
{% for post in recent_post_list %}
<li>
<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</li>
{% empty %}
暂无文章!
{% endfor %}
</ul>
</div>
和最新文章类似,先写逻辑在写html页面
# blog\templatetags\blog_extras.py
# 归档模板标签
'''
这里 Post.objects.dates 方法会返回一个列表,
列表中的元素为每一篇文章(Post)的创建时间(已去重),
且是 Python 的 date 对象,精确到月份,降序排列。
接受的三个参数值表明了这些含义,一个是 created_time ,
即 Post 的创建时间,month 是精度,order='DESC' 表明降序排列(即离当前越近的时间越排在前面)
。例如我们写了 3 篇文章,分别发布于 2019 年 2 月 21 日、2019 年 3 月 25 日、2019 年 3 月 28 日
,那么 dates 函数将返回 2019 年 3 月 和 2019 年 2 月这样一个时间列表,且降序排列,从而帮助我们实现按月归档的目的。
'''
@register.inclusion_tag('blog/inclusions/_archives.html', takes_context=True)
def show_archives(context):
return {'date_list': Post.objects.dates('created_time', 'month', order='DESC'),}
现在编写页面,在inclusions文件夹下创建 _archives.html 文件
# templates\blog\inclusions\_archives.html
<div class="widget widget-archives">
<h3 class="widget-title">归档</h3>
<ul>
{% for date in date_list %}
<li>
<a href="#">{{ date.year }} 年 {{ date.month }} 月</a>
</li>
{% empty %}
暂无归档!
{% endfor %}
</ul>
</div>
逻辑
# blog\templatetags\blog_extras.py
@register.inclusion_tag('blog/inclusions/_categories.html', takes_context=True)
def show_categories(context):
return {'category_list': Category.objects.all(), }
html页面,在inclusions文件夹下创建 _categories.html 文件
# templates\blog\inclusions\_categories.html
<div class="widget widget-category">
<h3 class="widget-title">分类</h3>
<ul>
{% for category in category_list %}
<li>
<a href="#">{{ category.name }} <span class="post-count">(13)</span></a>
</li>
{% empty %}
暂无分类!
{% endfor %}
</ul>
</div>
(11)
那个11目前还不是动态的,后面会介绍。
逻辑
# blog\templatetags\blog_extras.py
# 标签云模板标签
@register.inclusion_tag('blog/inclusions/_tags.html', takes_context=True)
def show_tags(context):
return {'tag_list': Tag.objects.all(),}
html页面,在inclusions文件夹下创建 _tags.html 文件
# templates\blog\inclusions\_tags.html
<div class="widget widget-tag-cloud">
<h3 class="widget-title">标签云</h3>
<ul>
{% for tag in tag_list %}
<li>
<a href="#">{{ tag.name }}</a>
</li>
{% empty %}
暂无标签!
{% endfor %}
</ul>
</div>
首选先导入自定义模板标签
# templates\base.html
{% load static %}
{% load blog_extras %}
<!DOCTYPE html>
<html>
...
</html>
然后找都侧边栏进行替换
# templates\base.html
<aside class="col-md-4">
{% block toc %}
{% endblock toc %}
{% show_recent_posts %}
{% show_archives %}
{% show_categories %}
{% show_tags %}
<div class="rss">
<a href=""><span class="ion-social-rss-outline"></span> RSS 订阅</a>
</div>
</aside>
然后打开index.html删除之前写死的数据
也就是
{% block toc %}
这里的代码全部删除
{% endblock %}
现在运行下看下效果就可以了。
上周回老家了,停了几天没更新,估计还有最多两周也就更新完了,还是有收获的。2019/11/25
上一章我们写了侧边栏,但是只有一个最新文章是可以点的,其余的归档分类和标签都是不能正常显示内容的。今天我们就把剩下的工作完成
记住一个顺序,是我自己喜欢的顺序,先写视图函数,然后在写路由,最后写html页面,分类页面和标签页面都是按照这个顺序来。
咱们就按照这个来,先写视图函数。
# blog\views.py
···
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', context={'post_list': post_list})
视图函数完事是路由
# blog\urls.py
urlpatterns = [
···
path('posts//' , views.detail, name='detail'),
# 归档路由
path('archives///' , views.archive, name='archive'),
]
最后是html页面
# templates\blog\inclusions\_archives.html
...
{% for date in date_list %}
<li>
<a href="{% url 'blog:archive' date.year date.month %}">
{{ date.year }} 年 {{ date.month }} 月
</a>
</li>
{% endfor %}
...
写完归档启动项目测试下吧。归档页面我截下图,分类页面和标签页面我就不截图了,一块写完一起启动测试吧。
注意我圈起来的地方,点击完归档路由是有变化的。
这三个都是类似的,我就不一一介绍了,代码里有注释。
分类视图函数
# blog\views.py
from .models import Post, Category, Tag
···
# 分类视图函数
def category(request, pk):
# 先导入404页面,记得导入Category类,和Tag类,提前做准备
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', context={'post_list': post_list})
分类路由
# blog\urls.py
urlpatterns = [
···
# 分类路由
path('categories//' , views.category, name='category'),
]
分类html页面
# templates\blog\inclusions\_categories.html
...
{% for category in category_list %}
<li>
<a href="{% url 'blog:category' category.pk %}">{{ category.name }}</a>
</li>
{% endfor %}
...
标签视图函数
# blog\views.py
···
# 标签视图函数
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', context={'post_list': post_list})
标签路由
# blog\urls.py
urlpatterns = [
···
# 标签路由
path('tag//' , views.tag, name='tag')
]
标签html页面
# templates\blog\inclusions\_tags.html
...
{% for tag in tag_list %}
<li>
<a href="{% url 'blog:tag' tag.pk %}">{{ tag.name }}</a>
</li>
{% empty %}
暂无标签!
{% endfor %}
...
侧边栏的功能基本实现了,明天要去我朋友那,周末要回老家,所以今天可能是这周更新的最后一篇。
2019/11/27
在虚拟环境下执行以下命令创建应用。应用名叫comments
python manage.py startapp comments
创建完之后注册app
# blogproject\settings.py
...
INSTALLED_APPS = [
...
'blog.apps.BlogConfig', # 后台注册博客
'comments.apps.CommentsConfig', # 注册 comments 应用
]
...
然后在admin后台显示中文名称
# comments\app.py
from django.apps import AppConfig
class CommentsConfig(AppConfig):
name = 'comments'
verbose_name = '评论'
直接上代码吧,这个模型的字段之前都解释过是,应该很好理解,如果还有不懂得直接去看一. 新建项目的的 1-2创建数据模型
# comments\models.py
from django.db import models
from django.utils import timezone
class Comment(models.Model):
name = models.CharField(max_length=50, verbose_name='名字')
email = models.EmailField(verbose_name='邮箱')
# blank=True允许为空
url = models.URLField(blank=True, verbose_name='网址')
text = models.TextField(verbose_name='内容')
created_time = models.DateTimeField(verbose_name='创建时间', 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])
然后数据库模型迁移,进入到虚拟环境中,依次执行以下两条命令
python manage.py makemigrations
python manage.py migrate
还是直接上代码,如何记得不是很清楚了去看五. 后台管理下的5-3. 汉化后台管理
# comments\admin.py
from django.contrib import admin
from .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)
启动项目查看下效果如下。
明天更新评论的表单,今天就先写到这吧,上周更新的很少,争取这周多更新点。2019/12/2
看了下已经两周没有更新了。今天才更新的。
表单通俗的讲就是前用户在页面上输入的,需要保存,所以用表单的形式进行提交到后端。
在 comments 目录下新建一个 forms.py
# comments\forms.py
from django import forms
from .models import Comment
# 评论表单类。
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
# 需要的字段,评论者,邮箱网址和评论内容
fields = ['name', 'email', 'url', 'text']
这个就之前的自定义标签一样了,可以回顾下第十章。
在 comments 文件夹下新建一个 templatetags 包里面__init__.py
然后在templatetags里面创建comments_extras.py文件和__init__.py平级
目录结构如下图
comments_extras.py代码如下:
# comments\templatetags\comments_extras.py
from django import template
from ..forms import CommentForm
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, }
然后在 templates/comments/inclusions 目录下新建一个 _form.html 模板,写上代码:
# templates\comments\inclusions\_form.html
<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>
</form>
接下来就是下修改详情页代码,显示空表单
# templates\blog\detail.html
{% extends 'base.html' %}
{% load comments_extras %}
...
<h3>发表评论</h3>
{% show_comment_form post %}
这还不能显示得配置下路由才可以。
在 comment 应用中再建一个 urls.py 文件,写入代码:
# comments\urls.py
from django.urls import path
from . import views
app_name = 'comments'
urlpatterns = [
path('comment/' , views.comment, name='comment'),
]
然后在项目的urls.py里进行配置
# \Django_blog\urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
···
# 配置评论路由
path('', include('comments.urls')),
]
上面只是显示一个空白的表单但是还不能提交。也不能录入到数据库了,设计下视图函数实现这个功能。
在comments应用的views.py里面写逻辑
# comments\views.py
from blog.models import Post
from django.shortcuts import get_object_or_404, redirect, render
from django.views.decorators.http import require_POST
from .forms import CommentForm
@require_POST
def comment(request, post_pk):
# 先获取被评论的文章,因为后面需要把评论和被评论的文章关联起来。
# 这里我们使用了 django 提供的一个快捷函数 get_object_or_404,
# 这个函数的作用是当获取的文章(Post)存在时,则获取;否则返回 404 页面给用户。
post = get_object_or_404(Post, pk=post_pk)
# django 将用户提交的数据封装在 request.POST 中,这是一个类字典对象。
# 我们利用这些数据构造了 CommentForm 的实例,这样就生成了一个绑定了用户提交数据的表单。
form = CommentForm(request.POST)
# 当调用 form.is_valid() 方法时,django 自动帮我们检查表单的数据是否符合格式要求。
if form.is_valid():
# 检查到数据是合法的,调用表单的 save 方法保存数据到数据库,
# commit=False 的作用是仅仅利用表单的数据生成 Comment 模型类的实例,但还不保存评论数据到数据库。
comment = form.save(commit=False)
# 将评论和被评论的文章关联起来。
comment.post = post
# 最终将评论数据保存进数据库,调用模型实例的 save 方法
comment.save()
# 重定向到 post 的详情页,实际上当 redirect 函数接收一个模型的实例时,它会调用这个模型实例的 get_absolute_url 方法,
# 然后重定向到 get_absolute_url 方法返回的 URL。
return redirect(post)
# 检查到数据不合法,我们渲染一个预览页面,用于展示表单的错误。
# 注意这里被评论的文章 post 也传给了模板,因为我们需要根据 post 来生成表单的提交地址。
context = {
'post': post,
'form': form,
}
return render(request, 'comments/preview.html', context=context)
有视图函数就有HTML页面
在templates下的comments下新建preview.html
# templates\comments\preview.html
{% extends 'base.html' %}
{% load comments_extras %}
{% block main %}
{% show_comment_form post form %}
{% endblock main %}
视图函数写完了,接下来就是发送消息了,并且储存到数据库
在视图函数中增加以下几行代码。
# comments\views.py
# 导入Django自带的messages相关配置
from django.contrib import messages
if form.is_valid():
...
comment.save()
# 评论成功之后直接给个提示消息
messages.add_message(request, messages.SUCCESS, '评论发表成功!', extra_tags='success')
return redirect(post)
context = {
'post': post,
'form': form,
}
# 评论失败也会给个提示消息
messages.add_message(request, messages.ERROR, '评论发表失败!请修改表单中的错误后重新提交。', extra_tags='danger')
然后在base.html增加以下几行代码。这段代码是在body里面的。如果找不到位置,可以参考我GitHub上的源码。
# templates\base.html
<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 %}
也是使用自定义模板标签
在comments_extras.py 下增加一个自定义的模板标签
# comments\templatetags\comments_extras.py
# 显示评论内容自定义模板标签
@register.inclusion_tag('comments/inclusions/_list.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_count': comment_count,
'comment_list': comment_list,
}
然后在comments下的inclusions下新建 _list.html显示评论列表
# comments\inclusions\_list.html
<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>
最后把博客详情页里面的假数据换成自定义模板标签,就可以显示真实的评论了。detail.html
# templates\blog\detail.html
<h3>发表评论</h3>
{% show_comment_form post %}
<div class="comment-list-panel">
{% show_comments post %}
</div>
效果如下图
一周多都没有更新了,事太多了,这周事可能少一点了,争取明天把博客更新完,就还剩下一点点了。2019/12/16
这个就比较简单了。直接上代码就可以看的懂。
# blog\models.py
class Post(models.Model):
...
...
class Meta:
verbose_name = '文章'
verbose_name_plural = verbose_name
# 对文章进行排序,后插入的文章显示在最上面
ordering = ['-created_time']
在index.html下修改以下代码。
修改完以下代码就可以index页就可以显示正常的评论量了,但是详情页还不行。
# templates\blog\index.html
<div class="entry-meta">
...
<span class="comments-link"><a href="{{ post.get_absolute_url }}#comment-area">{{ post.comment_set.count }} 评论</a></span>
<span class="views-count"><a href="{{ post.get_absolute_url }}">{{ post.views }} 阅读</a></span>
</div>
在detail.html下修改代码,使详情页也可以显示正确的评论量。
# templates\blog\detail.html
<header class="entry-header">
<h1 class="entry-title">{{ post.title }}</h1>
...
<span class="comments-link"><a href="#comment-area">{{ post.comment_set.count }} 评论</a></span>
<span class="views-count"><a href="#">{{ post.views }} 阅读</a></span>
</div>
</header>
至此Django写的一个小型的博客编写完成,经理了很长的时间,但是这其中我的收获也是很多的。最后在附上我的源码地址吧,
源码地址:https://github.com/Buddhistwang/Django_blog