Django搭建博客网站(1)

news/2024/5/9 13:45:33/文章来源:https://blog.csdn.net/fengbansha/article/details/85102713

一、前言

1.1.环境

python版本:3.6

Django版本:1.11.6

1.2.预览效果

最终搭建的blog的样子,基本上满足需求了。框架搭好了,至于CSS,可以根据自己喜好随意搭配。

二、建立博客应用

2.1.建立项目和应用

创建工程blogproject

django-admin startproject blogproject

创建blog应用

python manage.py startpapp blog

打开 blogproject\ 目录下的 settings.py 文件,找到 INSTALLED_APPS 设置项,将 blog 应用添加进去。

INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','blog',
]

2.2.目录结构

三、创建blog的数据库模型

3.1.设计博客的数据库表结构

博客最主要的功能就是展示我们写的文章,它需要从某个地方获取博客文章数据才能把文章展示出来,通常来说这个地方就是数据库。我们把写好的文章永久地保存在数据库里,当用户访问我们的博客时,Django 就去数据库里把这些数据取出来展现给用户。

博客的文章应该含有标题、正文、作者、发表时间等数据。一个更加现代化的博客文章还希望它有分类、标签、评论等。为了更好地存储这些数据,我们需要合理地组织数据库的表结构。

 我们的博客初级版本主要包含博客文章,文章会有分类以及标签。一篇文章只能有一个分类,但可以打上很多标签。我们把分类和标签做成单独的数据库表,再把文章和分类、标签关联起来。下面分别是分类和标签的数据库表:

 分类id    分类名

     1         python

     2         Django

标签id      标签名

     1          python学习

     2          Django学习

3.2.编写博客模型代码

 分类数据库表:

# blog/models.py

from django.db import models

class Category(models.Model):
name = models.CharField(max_length=100)

Category 就是一个标准的 Python 类,它继承了 models.Model 类,类名为 Category 。Category 类有一个属性 name,它是 models.CharField 的一个实例。

我们需要 3 个表格:文章(Post)、分类(Category)以及标签(Tag),下面就来分别编写它们对应的 Python 类。模型的代码通常写在相关应用的 models.py 文件里

# blog/models.py

from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
‘’
Django 要求模型必须继承 models.Model 类。
Category 只需要一个简单的分类名 name 就可以了。
CharField 指定了分类名 name 的数据类型,CharField 是字符型,
CharField 的 max_length 参数指定其最大长度,超过这个长度的分类名就不能被存入数据库。
‘’
name = models.CharField(max_length=100)

class Tag(models.Model):
‘’‘标签’’’

name <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">CharField</span><span class="token punctuation">(</span>max_length<span class="token operator">=</span><span class="token number">100</span><span class="token punctuation">)</span>

class Post(models.Model):
‘’‘文章’’’

# 文章标题
title <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">CharField</span><span class="token punctuation">(</span>max_length<span class="token operator">=</span><span class="token number">70</span><span class="token punctuation">)</span># 文章正文,我们使用了 TextField。
# 存储比较短的字符串可以使用 CharField,但对于文章的正文来说可能会是一大段文本,因此使用 TextField 来存储大段文本。
body <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">TextField</span><span class="token punctuation">(</span><span class="token punctuation">)</span># 这两个列分别表示文章的创建时间和最后一次修改时间,存储时间的字段用 DateTimeField 类型。
created_time <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">DateTimeField</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
modified_time <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">DateTimeField</span><span class="token punctuation">(</span><span class="token punctuation">)</span># 文章摘要,可以没有文章摘要,但默认情况下 CharField 要求我们必须存入数据,否则就会报错。
# 指定 CharField 的 blank<span class="token operator">=</span>True 参数值后就可以允许空值了。
excerpt <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">CharField</span><span class="token punctuation">(</span>max_length<span class="token operator">=</span><span class="token number">200</span><span class="token punctuation">,</span>blank<span class="token operator">=</span>True<span class="token punctuation">)</span># 我们在这里把文章对应的数据库表和分类、标签对应的数据库表关联了起来,但是关联形式稍微有点不同。
# 我们规定一篇文章只能对应一个分类,但是一个分类下可以有多篇文章,所以我们使用的是 ForeignKey,即一对多的关联关系。
# 而对于标签来说,一篇文章可以有多个标签,同一个标签下也可能有多篇文章,所以我们使用 ManyToManyField,表明这是多对多的关联关系。
# 同时我们规定文章可以没有标签,因此为标签 tags 指定了 blank<span class="token operator">=</span>True。
category <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">ForeignKey</span><span class="token punctuation">(</span>Category<span class="token punctuation">,</span>on_delete<span class="token operator">=</span>models<span class="token punctuation">.</span>CASCADE<span class="token punctuation">)</span>
tags <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">ManyToManyField</span><span class="token punctuation">(</span>Tag<span class="token punctuation">,</span>blank<span class="token operator">=</span>True<span class="token punctuation">)</span># 文章作者,这里 User 是从 django<span class="token punctuation">.</span>contrib<span class="token punctuation">.</span>auth<span class="token punctuation">.</span>models 导入的。
# django<span class="token punctuation">.</span>contrib<span class="token punctuation">.</span>auth 是 Django 内置的应用,专门用于处理网站用户的注册、登录等流程,User 是 Django 为我们已经写好的用户模型。
# 这里我们通过 ForeignKey 把文章和 User 关联了起来。
# 因为我们规定一篇文章只能有一个作者,而一个作者可能会写多篇文章,因此这是一对多的关联关系,和 Category 类似。
author <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">ForeignKey</span><span class="token punctuation">(</span>User<span class="token punctuation">,</span>on_delete<span class="token operator">=</span>models<span class="token punctuation">.</span>CASCADE<span class="token punctuation">)</span></pre><h2>四、迁移数据库</h2><h3>4.1.设置数据库为Mysql</h3><p>更改setting.py默认配置</p><pre class="prism-token token  language-javascript"># DATABASES <span class="token operator">=</span> <span class="token punctuation">{</span>

‘default’: {

‘ENGINE’: ‘django.db.backends.sqlite3’,

‘NAME’: os.path.join(BASE_DIR, ‘db.sqlite3’),

}

}

DATABASES = {
‘default’: {
‘ENGINE’: ‘django.db.backends.mysql’,
‘NAME’: ‘django’, #数据库名字
‘USER’: ‘root’, #账号
‘PASSWORD’: ‘123456’, #密码
‘HOST’: ‘127.0.0.1’, #IP
‘PORT’: ‘3306’, #端口
}
}

导入Pymysql

# blog/init.py

import pymysql
pymysql.install_as_MySQLdb()

4.2.迁移数据库

分别运行下面两条命令

python manage.py makemigrations 
python manage.py migrate

当我们执行了 python manage.py makemigrations 后,Django 在 blog 应用的 migrations\ 目录下生成了一个 0001_initial.py 文件,这个文件是 Django 用来记录我们对模型做了哪些修改的文件。目前来说,我们在 models.py 文件里创建了 3 个模型类,Django 把这些变化记录在了 0001_initial.py 里。

不过此时还只是告诉了 Django 我们做了哪些改变,为了让 Django 真正地为我们创建数据库表,接下来又执行了 python manage.py migrate 命令。Django 通过检测应用中 migrations\ 目录下的文件,得知我们对数据库做了哪些操作,然后它把这些操作翻译成数据库操作语言,从而把这些操作作用于真正的数据库。

你可以看到命令的输出除了 Applying blog.0001_initial… OK 外,Django 还对其它文件做了操作。这是因为除了我们自己建立的 blog 应用外,Django 自身还内置了很多应用,这些应用本身也是需要存储数据的。可以在 settings.py 的 INSTALLED_APP 设置里看到这些应用,当然我们目前不必关心这些。

#blogproject/settings.py

INSTALLED_APPS = [
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
‘blog’,
]

运行下面的命令将看到输出了经 Django 翻译后的数据库表创建语句,这有助于你理解 Django ORM 的工作机制。

python manage.py sqlmigrate blog 0001

4.3.存数据

打开一个交互式命令行

python manage.py shell

首先我们来创建一个分类和一个标签:

我们首先导入 3 个之前写好的模型类,然后实例化了一个 Category 类和一个 Tag 类,为他们的属性 name 赋了值。为了让 Django 把这些数据保存进数据库,调用实例的 save方法即可。

创建文章之前,我们需要先创建一个 User,用于指定文章的作者。创建 User 的命令 Django 已经帮我们写好了,依然是通过 manage.py 来运行。首先exit()退出命令交互栏,运行 python manage.py createsuperuser 命令并根据提示创建用户:用户名,邮箱,密码

再次运行 python manage.py shell 进入 Python 命令交互栏,开始创建文章:

由于我们重启了 shell,因此需要重新导入了 CategoryTagPost 以及 User。我们还导入了一个 Django 提供的辅助模块 timezone,这是因为我们需要调用它的 now() 方法为 created_time 和 modified_time 指定时间,容易理解 now 方法返回当前时间。然后我们根据用户名和分类名,通过 get 方法取出了存在数据库中的 User 和 Category(取数据的方法将在下面介绍)。接着我们为文章指定了 titlebody 、created_timemodified_time值,并把它和前面创建的 Category 以及 User 关联了起来。允许为空 excerpttags 我们就没有为它们指定值了。

4.4.取数据

 数据已经存入数据库了,现在要把它们取出来看看:

objects 是我们的模型管理器,它为我们提供一系列从数据库中取数据方法,这里我们使用了 all 方法,表示我们要把对应的数据全部取出来。可以看到 all 方法都返回了数据,这些数据应该是我们之前存进去的,但是显示的字符串有点奇怪,无法看出究竟是不是我们之前存入的数据。为了让显示出来的数据更加人性化一点,我们为 3 个模型分别增加一个 str 方法:

# blog/models.py

from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
‘’
Django 要求模型必须继承 models.Model 类。
Category 只需要一个简单的分类名 name 就可以了。
CharField 指定了分类名 name 的数据类型,CharField 是字符型,
CharField 的 max_length 参数指定其最大长度,超过这个长度的分类名就不能被存入数据库。
‘’
name = models.CharField(max_length=100)

def <span class="token function">__str__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token keyword">return</span> self<span class="token punctuation">.</span>name

class Tag(models.Model):
‘’‘标签’’’

name <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">CharField</span><span class="token punctuation">(</span>max_length<span class="token operator">=</span><span class="token number">100</span><span class="token punctuation">)</span>def <span class="token function">__str__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token keyword">return</span> self<span class="token punctuation">.</span>name

class Post(models.Model):
‘’‘文章’’’

# 文章标题
title <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">CharField</span><span class="token punctuation">(</span>max_length<span class="token operator">=</span><span class="token number">70</span><span class="token punctuation">)</span># 文章正文,我们使用了 TextField。
# 存储比较短的字符串可以使用 CharField,但对于文章的正文来说可能会是一大段文本,因此使用 TextField 来存储大段文本。
body <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">TextField</span><span class="token punctuation">(</span><span class="token punctuation">)</span># 这两个列分别表示文章的创建时间和最后一次修改时间,存储时间的字段用 DateTimeField 类型。
created_time <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">DateTimeField</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
modified_time <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">DateTimeField</span><span class="token punctuation">(</span><span class="token punctuation">)</span># 文章摘要,可以没有文章摘要,但默认情况下 CharField 要求我们必须存入数据,否则就会报错。
# 指定 CharField 的 blank<span class="token operator">=</span>True 参数值后就可以允许空值了。
excerpt <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">CharField</span><span class="token punctuation">(</span>max_length<span class="token operator">=</span><span class="token number">200</span><span class="token punctuation">,</span>blank<span class="token operator">=</span>True<span class="token punctuation">)</span># 我们在这里把文章对应的数据库表和分类、标签对应的数据库表关联了起来,但是关联形式稍微有点不同。
# 我们规定一篇文章只能对应一个分类,但是一个分类下可以有多篇文章,所以我们使用的是 ForeignKey,即一对多的关联关系。
# 而对于标签来说,一篇文章可以有多个标签,同一个标签下也可能有多篇文章,所以我们使用 ManyToManyField,表明这是多对多的关联关系。
# 同时我们规定文章可以没有标签,因此为标签 tags 指定了 blank<span class="token operator">=</span>True。
category <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">ForeignKey</span><span class="token punctuation">(</span>Category<span class="token punctuation">,</span>on_delete<span class="token operator">=</span>models<span class="token punctuation">.</span>CASCADE<span class="token punctuation">)</span>
tags <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">ManyToManyField</span><span class="token punctuation">(</span>Tag<span class="token punctuation">,</span>blank<span class="token operator">=</span>True<span class="token punctuation">)</span># 文章作者,这里 User 是从 django<span class="token punctuation">.</span>contrib<span class="token punctuation">.</span>auth<span class="token punctuation">.</span>models 导入的。
# django<span class="token punctuation">.</span>contrib<span class="token punctuation">.</span>auth 是 Django 内置的应用,专门用于处理网站用户的注册、登录等流程,User 是 Django 为我们已经写好的用户模型。
# 这里我们通过 ForeignKey 把文章和 User 关联了起来。
# 因为我们规定一篇文章只能有一个作者,而一个作者可能会写多篇文章,因此这是一对多的关联关系,和 Category 类似。
author <span class="token operator">=</span> models<span class="token punctuation">.</span><span class="token function">ForeignKey</span><span class="token punctuation">(</span>User<span class="token punctuation">,</span>on_delete<span class="token operator">=</span>models<span class="token punctuation">.</span>CASCADE<span class="token punctuation">)</span>def <span class="token function">__str__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token keyword">return</span> self<span class="token punctuation">.</span>title</pre><p>定义好&nbsp;<code>__str__</code>&nbsp;方法后,解释器显示的内容将会是&nbsp;<code>__str__</code>&nbsp;方法返回的内容。这里&nbsp;<code>Category</code>&nbsp;返回分类名&nbsp;<code>name</code>&nbsp;,<code>Tag</code>&nbsp;返回标签名,而&nbsp;<code>Post</code>&nbsp;返回它的&nbsp;<code>title</code>。</p><p>&nbsp;exit() 退出 Shell,再重新运行&nbsp;<code>python manage.py shell</code>&nbsp;进入 Shell。</p><figure><div class="image-block"><span class="lazy-image-holder" dataurl="https://ask.qcloudimg.com/http-save/yehe-1626007/f6dhyj60ct.png"></span></div></figure><p>可以看到返回的是我们之前存入的数据。</p><p>此外我们在创建文章时提到了通过&nbsp;<code>get</code>&nbsp;方法来获取数据,这里&nbsp;<code>all</code>&nbsp;方法和&nbsp;<code>get</code>&nbsp;方法的区别是:<code>all</code>&nbsp;方法返回全部数据,是一个类似于列表的数据结构(QuerySet);而&nbsp;<code>get</code>&nbsp;返回一条记录数据,如有多条记录或者没有记录,<code>get</code>&nbsp;方法均会抛出相应异常。</p><h2>&nbsp;五、博客首页视图</h2><h3>5.1.Django处理HTTP请求</h3><p>Web 应用的交互过程其实就是 HTTP 请求与响应的过程。无论是在 PC 端还是移动端,我们通常使用浏览器来上网,上网流程大致来说是这样的:</p><ol class="ol-level-0"><li>我们打开浏览器,在地址栏输入想访问的网址,比如 http://www.cnblogs.com/。</li><li>浏览器知道我们想要访问哪个网址后,它在后台帮我们做了很多事情。主要就是把我们的访问意图包装成一个 HTTP 请求,发给我们想要访问的网址所对应的服务器。通俗点说就是浏览器帮我们通知网站的服务器,说有人来访问你啦,访问的请求都写在 HTTP 里了,你按照要求处理后告诉我,我再帮你回应他!</li><li>服务器处理了HTTP 请求,然后生成一段 HTTP 响应给浏览器。浏览器解读这个响应,把相关的内容在浏览器里显示出来,于是我们就看到了网站的内容。比如你访问了我的博客主页http://www.cnblogs.com/derek1184405959/,服务器接收到这个请求后就知道用户访问的是首页,首页显示的是全部文章列表,于是它从数据库里把文章数据取出来,生成一个写着这些数据的 HTML 文档,包装到 HTTP 响应里发给浏览器,浏览器解读这个响应,把 HTML 文档显示出来,我们就看到了文章列表的内容。</li></ol><p>因此,Django 作为一个 Web 框架,它的使命就是处理流程中的第二步。即接收浏览器发来的 HTTP 请求,返回相应的 HTTP 响应。于是引出这么几个问题:</p><ol class="ol-level-0"><li>Django 如何接收 HTTP 请求?</li><li>Django 如何处理这个 HTTP 请求?</li><li>Django 如何生成 HTTP 响应?</li></ol><p>对于如何处理这些问题,Django 有其一套规定的机制。我们按照 Django 的规定,就能开发出所需的功能</p><p><strong>Hello视图函数</strong></p><p>&nbsp;我们先以一个最简单的 Hello World 为例来看看 Django 处理上述问题的机制是怎么样的。</p><p> <strong>绑定url和视图函数</strong></p><p>&nbsp;首先 Django 需要知道当用户访问不同的网址时,应该如何处理这些不同的网址(即所说的路由)。Django 的做法是把不同的网址对应的处理函数写在一个 urls.py 文件里,当用户访问某个网址时,Django 就去会这个文件里找,如果找到这个网址,就会调用和它绑定在一起的处理函数(叫做视图函数)。</p><p>&nbsp;下面是具体的做法,首先在 blog 应用的目录下创建一个 urls.py 文件,在 blog\urls.py 中写入这些代码:</p><pre class="prism-token token  language-javascript"># blog<span class="token operator">/</span>urls<span class="token punctuation">.</span>py

from django.conf.urls import url
from . import views

urlpatterns = [
url(r’^KaTeX parse error: Expected 'EOF', got '&' at position 377: …o.conf.urls 导入了&̲nbsp;<code>url<…’ 的模式正是匹配一个空字符串(这个正则表达式的意思是以空字符串开头且以空字符串结尾),于是二者匹配,Django 便会调用其对应的 views.index 函数。

注意:在项目根目录的 blogproject\ 目录下(即 settings.py 所在的目录),原本就有一个 urls.py 文件,这是整个工程项目的 URL 配置文件。而我们这里新建了一个 urls.py 文件,且位于 blog 应用下。这个文件将用于 blog 应用相关的 URL 配置。不要把两个文件搞混了。

编写视图函数

第二步就是要实际编写我们的 views.index 视图函数了,按照惯例视图函数定义在 views.py 文件里:

from django.shortcuts import HttpResponse

def index(request):
return HttpResponse(‘欢迎访问我的博客’)

我们前面说过,Web 服务器的作用就是接收来自用户的 HTTP 请求,根据请求内容作出相应的处理,并把处理结果包装成 HTTP 响应返回给用户。

这个两行的函数体现了这个过程。它首先接受了一个名为 request 的参数,这个 request就是 Django 为我们封装好的 HTTP 请求,它是类 HttpRequest 的一个实例。然后我们便直接返回了一个 HTTP 响应给用户,这个 HTTP 响应也是 Django 帮我们封装好的,它是类 HttpResponse 的一个实例,只是我们给它传了一个自定义的字符串参数。

浏览器接收到这个响应后就会在页面上显示出我们传递的内容:欢迎访问我的博客

配置项目URL

还差最后一步了,我们前面建立了一个 urls.py 文件,并且绑定了 URL 和视图函数 index,但是 Django 并不知道。Django 匹配 URL 模式是在 blogproject\ 目录(即 settings.py 文件所在的目录)的 urls.py 下的,所以我们要把 blog 应用下的 urls.py 文件包含到 blogproject\urls.py 里去:

# blogproject/urls.py

from django.contrib import admin
from django.conf.urls import url,include

urlpatterns = [
url(‘admin/’, admin.site.urls),
url(’’, include(‘blog.urls’)),
]

我们这里导入了一个 include 函数,然后利用这个函数把 blog 应用下的 urls.py 文件包含了进来。此外 include 前还有一个 r’’,这是一个空字符串。这里也可以写其它字符串,Django 会把这个字符串和后面 include 的 urls.py 文件中的 URL 拼接。比如说如果我们这里把 r’’ 改成 r’blog/’,而我们在 blog.urls 中写的 URL 是 r’^$’,即一个空字符串。那么 Django 最终匹配的就是 blog/ 加上一个空字符串,即 blog/。

运行结果

运行 python manage.py runserver 打开开发服务器,在浏览器输入开发服务器的地址 http://127.0.0.1:8000/,可以看到 Django 返回的内容了。

5.2.使用Django模板系统

这基本上就上 Django 的开发流程了,写好处理 HTTP 请求和返回 HTTP 响应的视图函数,然后把视图函数绑定到相应的 URL 上。

但是等一等!我们看到在视图函数里返回的是一个 HttpResponse 类的实例,我们给它传入了一个希望显示在用户浏览器上的字符串。但是我们的博客不可能只显示这么一句话,它有可能会显示很长很长的内容。比如我们发布的博客文章列表,或者一大段的博客文章。我们不能每次都把这些大段大段的内容传给 HttpResponse

Django 对这个问题给我们提供了一个很好的解决方案,叫做模板系统。Django 要我们把大段的文本写到一个文件里,然后 Django 自己会去读取这个文件,再把读取到的内容传给 HttpResponse。让我们用模板系统来改造一下上面的例子。

首先在我们的项目根目录(即 manage.py 文件所在目录)下建立一个名为 templates 的文件夹,用来存放我们的模板。然后在 templates\ 目录下建立一个名为 blog 的文件夹,用来存放和 blog 应用相关的模板。

当然模板存放在哪里是无关紧要的,只要 Django 能够找到的就好。但是我们建立这样的文件夹结构的目的是把不同应用用到的模板隔离开来,这样方便以后维护。我们在 templates\blog 目录下建立一个名为 index.html 的文件

在 templates\blog\index.html 文件里写入下面的代码:

<!DOCTYPE html>
<html lang=“en”>
<head>
<meta charset=“UTF-8”>
<title>{{ title }}</title>
</head>
<body>
<h1>{{ welcome }}</h1>
</body>
</html>

这是一个标准的 HTML 文档,只是里面有两个比较奇怪的地方:{{ title }}{{ welcome }}。这是 Django 规定的语法。用 {{ }} 包起来的变量叫做模板变量。Django 在渲染这个模板的时候会根据我们传递给模板的变量替换掉这些变量。最终在模板中显示的将会是我们传递的值。

模板写好了,还得告诉 Django 去哪里找模板,在 settings.py 文件里设置一下模板文件所在的路径。在 settings.py 找到 TEMPLATES 选项,其中 DIRS 就是设置模板的路径,在 [] 中写入 os.path.join(BASE_DIR, ‘templates’),即像下面这样:

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’,
],
},
},
]

这里 BASE_DIR 是 settings.py 在配置开头前面定义的变量,记录的是工程根目录 blogproject\ 的值(注意是最外层的 blogproject\ 目录)。在这个目录下有模板文件所在的目录 templates\,于是利用os.path.join 把这两个路径连起来,构成完整的模板路径,Django 就知道去这个路径下面找我们的模板了。

视图函数可以改一下了:

from django.http import HttpResponse
from django.shortcuts import render

def index(request):
return render(request, ‘blog/index.html’, context={
‘title’: ‘我的博客首页’,
‘welcome’: ‘欢迎访问我的博客首页’
})

这里我们不再是直接把字符串传给 HttpResponse 了,而是调用 Django 提供的 render 函数。这个函数根据我们传入的参数来构造 HttpResponse

我们首先把 HTTP 请求传了进去,然后 render 根据第二个参数的值 blog/index.html 找到这个模板文件并读取模板中的内容。之后 render 根据我们传入的 context 参数的值把模板中的变量替换为我们传递的变量的值,{{ title }} 被替换成了 context 字典中 title对应的值,同理 {{ welcome }} 也被替换成相应的值。

最终,我们的 HTML 模板中的内容字符串被传递给 HttpResponse 对象并返回给浏览器(Django 在 render 函数里隐式地帮我们完成了这个过程),这样用户的浏览器上便显示出了我们写的 HTML 模板的内容

六、真正的Django博客首页视图

 在此之前我们已经编写了 Blog 的首页视图,并且配置了 URL 和模板,让 Django 能够正确地处理 HTTP 请求并返回合适的 HTTP 响应。不过我们仅仅在首页返回了一句话:欢迎访问我的博客。这是个 Hello World 级别的视图函数,我们需要编写真正的首页视图函数,当用户访问我们的博客首页时,他将看到我们发表的博客文章列表。像前面演示的那样

6.1.首页视图函数

上一节我们阐明了 Django 的开发流程。即首先配置 URL,把 URL 和相应的视图函数绑定,一般写在 urls.py 文件里,然后在工程的 urls.py 文件引入。其次是编写视图函数,视图中需要渲染模板,我们也在 settings.py 中进行了模板相关的配置,让 Django 能够找到需要渲染的模板。最后把渲染完成的 HTTP 响应返回就可以了。相关的配置和准备工作都在之前完成了,这里我们只需专心编写视图函数,让它实现我们想要的功能即可。

首页的视图函数其实很简单,代码像这样:

# blog/views.py

from django.shortcuts import render
from . models import Post

def index(request):
post_list = Post.objects.all().order_by(’-created_time’)
return render(request,‘blog/index.html’,{‘post_list’:post_list})

我们曾经在前面的章节讲解过模型管理器 objects 的使用。这里我们使用 all() 方法从数据库里获取了全部的文章,存在了 post_list 变量里。all 方法返回的是一个 QuerySet(可以理解成一个类似于列表的数据结构),由于通常来说博客文章列表是按文章发表时间倒序排列的,即最新的文章排在最前面,所以我们紧接着调用了 order_by 方法对这个返回的 queryset 进行排序。排序依据的字段是 created_time,即文章的创建时间。- 号表示逆序,如果不加 - 则是正序。 接着如之前所做,我们渲染了 blog\index.html 模板文件,并且把包含文章列表数据的 post_list 变量传给了模板。

6.2.处理静态文件

我们的项目使用了从网上下载的一套博客模板 点击这里下载全套模板。这里面除了HTML 文档外,还包含了一些 CSS 文件和 JavaScript 文件以让网页呈现出我们现在看到的样式。同样我们需要对 Django 做一些必要的配置,才能让 Django 知道如何在开发服务器中引入这些 CSS 和 JavaScript 文件,这样才能让博客页面的 CSS 样式生效。

按照惯例,我们把 CSS 和 JavaScript 文件放在 blog 应用的 static\ 目录下。因此,先在 blog 应用下建立一个 static 文件夹。同时,为了避免和其它应用中的 CSS 和 JavaScript 文件命名冲突(别的应用下也可能有和 blog 应用下同名的 CSS 、JavaScript 文件),我们再在 static\ 目录下建立一个 blog 文件夹,把下载的博客模板中的 css 和 js 文件夹连同里面的全部文件一同拷贝进这个目录。目录结构

用下载的博客模板中的 index.html 文件替换掉之前我们自己写的 index.html 文件。如果你好奇,现在就可以运行开发服务器,看看首页是什么样子。

如图所示,你会看到首页显示的样式非常混乱,原因是浏览器无法正确加载 CSS 等样式文件。需要以 Django 的方式来正确地处理 CSS 和 JavaScript 等静态文件的加载路径。CSS 样式文件通常在 HTML 文档的 head 标签里引入,打开 index.html 文件,在文件的开始处找到 head 标签包裹的内容,大概像这样:

<head>
<title>Black &amp; White</title>
<span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span> meta <span class="token operator">--</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>meta charset<span class="token operator">=</span><span class="token string">"UTF-8"</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>meta name<span class="token operator">=</span><span class="token string">"viewport"</span> content<span class="token operator">=</span><span class="token string">"width=device-width, initial-scale=1"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span> css <span class="token operator">--</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>link rel<span class="token operator">=</span><span class="token string">"stylesheet"</span> href<span class="token operator">=</span><span class="token string">"css/bootstrap.min.css"</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>link rel<span class="token operator">=</span><span class="token string">"stylesheet"</span> href<span class="token operator">=</span><span class="token string">"http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>link rel<span class="token operator">=</span><span class="token string">"stylesheet"</span> href<span class="token operator">=</span><span class="token string">"css/pace.css"</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>link rel<span class="token operator">=</span><span class="token string">"stylesheet"</span> href<span class="token operator">=</span><span class="token string">"css/custom.css"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span> js <span class="token operator">--</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>script src<span class="token operator">=</span><span class="token string">"js/jquery-2.1.3.min.js"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>script src<span class="token operator">=</span><span class="token string">"js/bootstrap.min.js"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>script src<span class="token operator">=</span><span class="token string">"js/pace.min.js"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>script src<span class="token operator">=</span><span class="token string">"js/modernizr.custom.js"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>

</head>

CSS 样式文件的路径在 link 标签的 href 属性里,而 JavaScript 文件的路径在 script 标签的 src 属性里。可以看到诸如 `href=“css/bootstrap.min.css” 或者 src=“js/jquery-2.1.3.min.js” 这样的引用,由于引用文件的路径不对,所以浏览器引入这些文件失败。我们需要把它们改成正确的路径。把代码改成下面样子,正确地引入 static 文件下的 CSS 和 JavaScript 文件:

{% load staticfiles %}

<!DOCTYPE html>
<html>
<head>
<title>Black &amp; White</title>

<span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span> meta <span class="token operator">--</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>meta charset<span class="token operator">=</span><span class="token string">"UTF-8"</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>meta name<span class="token operator">=</span><span class="token string">"viewport"</span> content<span class="token operator">=</span><span class="token string">"width=device-width, initial-scale=1"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span> css <span class="token operator">--</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>link rel<span class="token operator">=</span><span class="token string">"stylesheet"</span> href<span class="token operator">=</span><span class="token string">"{% static 'blog/css/bootstrap.min.css' %}"</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>link rel<span class="token operator">=</span><span class="token string">"stylesheet"</span> href<span class="token operator">=</span><span class="token string">"http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>link rel<span class="token operator">=</span><span class="token string">"stylesheet"</span> href<span class="token operator">=</span><span class="token string">"{% static 'blog/css/pace.css' %}"</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>link rel<span class="token operator">=</span><span class="token string">"stylesheet"</span> href<span class="token operator">=</span><span class="token string">"{% static 'blog/css/custom.css' %}"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">!</span><span class="token operator">--</span> js <span class="token operator">--</span><span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>script src<span class="token operator">=</span><span class="token string">"{% static 'blog/js/jquery-2.1.3.min.js' %}"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>script src<span class="token operator">=</span><span class="token string">"{% static 'blog/js/bootstrap.min.js' %}"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>script src<span class="token operator">=</span><span class="token string">"{% static 'blog/js/pace.min.js' %}"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>
<span class="token operator">&lt;</span>script src<span class="token operator">=</span><span class="token string">"{% static 'blog/js/modernizr.custom.js' %}"</span><span class="token operator">&gt;</span><span class="token operator">&lt;</span><span class="token operator">/</span>script<span class="token operator">&gt;</span>

</head>

<body>

<! 其它内容 >

<script src="{% static ‘blog/js/script.js’ %}"></script>

</body>
</html>

我们把引用路径放在了一个奇怪的符号里,例如:href="{% static ‘blog/css/bootstrap.min.css’ %}"。用 {% %} 包裹起来的叫做模板标签。我们前面说过用 {{ }} 包裹起来的叫做模板变量,其作用是在最终渲染的模板里显示由视图函数传过来的变量值。而这里我们使用的模板标签的功能则类似于函数,例如这里的 static 模板标签,它把跟在后面的字符串 ‘css/bootstrap.min.css’ 转换成正确的文件引入路径。这样 css 和 js 文件才能被正确加载,样式才能正常显示。

为了能在模板中使用 {% static %} 模板标签,别忘了在最顶部添加 {% load staticfiles %} 。static 模板标签位于 staticfiles 模块中,只有通过 load 模板标签将该模块引入后,才能在模板中使用 {% static %} 标签。

替换完成后你可以刷新页面并看看网页的源代码,看一看 {% static %} 模板标签在页面渲染后究竟被替换成了什么样的值。例如我们可以看到

<link rel=“stylesheet” href="{% static ‘blog/css/pace.css’ %}">

这一部分最终在浏览器中显示的是:

<link rel=“stylesheet” href="/static/blog/css/pace.css">

这正是 pace.css 文件所在的路径,其它的文件路径也被类似替换。可以看到 {% static %} 标签的作用实际就是把后面的字符串加了一个 /static/ 前缀,比如 {% static ‘blog/css/pace.css’ %} 最终渲染的值是 /static/blog/css/pace.css。而 /static/ 前缀是我们在 settings.py 文件中通过 STATIC_URL = ‘/static/’ 指定的。事实上,如果我们直接把引用路径写成 /static/blog/css/pace.css 也是可以的,那么为什么要使用 {% static %} 标签呢?想一下,目前 URL 的前缀是 /static/,如果哪一天因为某些原因,我们需要把 /static/ 改成 /resource/,如果你是直接写的引用路劲而没有使用 static 模板标签,那么你可能需要改 N 个地方。如果你使用了 static 模板标签,那么只要在 settings.py 处改一个地方就可以了,即把 STATIC_URL = ‘/static/’ 改成 STATIC_URL = ‘/resource/’

注意这里有一个 CSS 文件的引入

<link rel=“stylesheet” href=“http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css”>

我们没有使用模板标签,因为这里的引用的文件是一个外部文件,不是我们项目里 static\blog\css\ 目录下的文件,因此无需使用模板标签。

正确引入了静态文件后样式显示正常了。

6.3修改模板

 目前我们看到的只是模板中预先填充的一些数据,我们得让它显示从数据库中获取的文章数据。下面来稍微改造一下模板:

在模板 index.html 中你会找到一系列 article 标签:

templates/blog/index.html


<article class=“post post-1”>

</article>

<article class=“post post-2”>

</article>

<article class=“post post-3”>

</article>

这里面包裹的内容显示的就是文章数据了。我们前面在视图函数 index 里给模板传了一个 post_list 变量,它里面包含着从数据库中取出的文章列表数据。就像 Python 一样,我们可以在模板中循环这个列表,把文章一篇篇循环出来,然后一篇篇显示文章的数据。要在模板中使用循环,需要使用到前面提到的模板标签,这次使用 {% for %} 模板标签。将 index.html 中多余的 article 标签删掉,只留下一个 article 标签,然后写上下列代码:

templates/blog/index.html


{% for post in post_list %}
<article class=“post post-{{ post.pk }}”>

</article>
{% empty %}
<div class=“no-post”>暂时还没有发布的文章!</div>
{% endfor %}

可以看到语法和 Python 的 for 循环类似,只是被 {% %} 这样一个模板标签符号包裹着。{% empty %} 的作用是当 post_list 为空,即数据库里没有文章时显示 {% empty %} 下面的内容,最后我们用 {% endfor %} 告诉 Django 循环在这里结束了。

你可能不太理解模板中的 post 和 post_list 是什么。post_list 是一个 QuerySet(类似于一个列表的数据结构),其中每一项都是之前定义在 blog\models.py 中的 Post 类的实例,且每个实例分别对应着数据库中每篇文章的记录。因此我们循环遍历 post_list ,每一次遍历的结果都保存在 post变量里。所以我们使用模板变量来显示 post 的属性值。例如这里的 {{ post.pk }}(pk 是 primary key 的缩写,即 post 对应于数据库中记录的 id 值,该属性尽管我们没有显示定义,但是 Django 会自动为我们添加)。

 我们把标题替换成 post 的 title 属性值。注意要把它包裹在模板变量里,因为它最终要被替换成实际的 title 值。

<h1 class=“entry-title”>
<a href=“single.html”>{{ post.title }}</a>
</h1>

下面这 5 个 span 标签里分别显示了分类(category)、文章发布时间、文章作者、评论数、阅读量。

<div class=“entry-meta”>
<span class=“post-category”><a href="#">Django 博客教程</a></span>
<span class=“post-date”><a href="#"><time class=“entry-date”
datetime=“2012-11-09T23:15:57+00:00”>2017511</time></a></span>
<span class=“post-author”><a href="#">追梦人物</a></span>
<span class=“comments-link”><a href="#">4 评论</a></span>
<span class=“views-count”><a href="#">588 阅读</a></span>
</div>

再次替换掉一些数据,由于评论数和阅读量暂时没法替换,因此先留着,我们在之后实现了这些功能后再来修改它,目前只替换分类、文章发布时间、文章作者:

<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>

这里 p 标签里显示的是摘要

<div class=“entry-content clearfix”>
<p>免费、中文、零基础,完整的项目,基于最新版 Django 1.10 和 Python 3.5。带你从零开始一步步开发属于自己的博客网站,帮助你以最快的速度掌握 Django
开发的技巧</p>
<div class=“read-more cl-effect-14”>
<a href="#" class=“more-link”>继续阅读 <span class=“meta-nav”></span></a>
</div>
</div>

替换成 post 的摘要:

<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>

再次访问首页,它显示:暂时还没有发布的文章!好吧,做了这么多工作,但是数据库中其实还没有任何数据呀!接下来我们就实际写几篇文章保存到数据库里,看看显示的效果究竟如何。

 七、在Django Admin后台发布文章

 在此之前我们完成了 Django 博客首页视图的编写,我们希望首页展示发布的博客文章列表,但是它却抱怨:暂时还没有发布的文章!如它所言,我们确实还没有发布任何文章,本节我们将使用 Django 自带的 Admin 后台来发布我们的博客文章。

7.1.在Django后台注册模型

前面已经用python manage.py createsuperuser,创建了超级用户。

要在后台注册我们自己创建的几个模型,这样 Django Admin 才能知道它们的存在,注册非常简单,只需要在 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/ ,就进入了到了Django Admin 后台登录页面,输入刚才创建的管理员账户密码就可以登录到后台了。

可以看到我们刚才注册的三个模型了,点击 Posts 后面的增加按钮,将进入添加 Post 的页面,也就是新增博客文章。然后在相关的地方输入一些测试用的内容,增加完后点击保存,这样文章就添加完毕了,你也可以多添加几篇看看效果。注意每篇文章必须有一个分类,在添加文章时你可以选择已有分类。如果数据库中还没有分类,在选择分类时点击 Category 后面的 + 按钮新增一个分类即可。

 访问 http://127.0.0.1:8000/ 首页,你就可以看到你添加的文章列表了,下面是我所在环境的效果图:

7.2.定制Admin后台

 在 admin post 列表页面,我们只看到了文章的标题,但是我们希望它显示更加详细的信息,这需要我们来定制 Admin 了,在 admin.py 添加如下代码:

# blog/admin.py

from django.contrib import admin

from .models import Post,Category,Tag

class PostAdmin(admin.ModelAdmin):
list_display = [‘title’, ‘created_time’, ‘modified_time’, ‘category’, ‘author’]

admin.site.register(Post,PostAdmin)
admin.site.register(Category)
admin.site.register(Tag)

这里只是为了简单地到达期望效果

刷新 Admin Post 列表页面,可以看到显示的效果好多了。

八、博客文章详情页

 首页展示的是所有文章的列表,当用户看到感兴趣的文章时,他点击文章的标题或者继续阅读的按钮,应该跳转到文章的详情页面来阅读文章的详细内容。现在让我们来开发博客的详情页面,有了前面的基础,开发流程都是一样的了:首先配置 URL,即把相关的 URL 和视图函数绑定在一起,然后实现视图函数,编写模板并让视图函数渲染模板。

8.1.设置文章详情页的url

回顾一下我们首页视图的 URL,在 blog\urls.py 文件里,我们写了:

blog/urls.py

from django.conf.urls import url

from . import views

urlpatterns = [
url(r’^$’, views.index, name=‘index’),
]

首页视图匹配的 URL 去掉域名后其实就是一个空的字符串。对文章详情视图而言,每篇文章对应着不同的 URL。比如我们可以把文章详情页面对应的视图设计成这个样子:当用户访问 <网站域名>/post/1/ 时,显示的是第一篇文章的内容,而当用户访问 <网站域名>/post/2/ 时,显示的是第二篇文章的内容,这里数字代表了第几篇文章,也就是数据库中 Post 记录的 id 值。下面依照这个规则来绑定 URL 和视图:

# blog/urls.py

from django.conf.urls import url
from . import views
app_name = ‘blog’

urlpatterns = [
url(r’^KaTeX parse error: Expected 'EOF', got '&' at position 423: …ing">'^post/(?P&̲lt;pk&gt;[0-9]+…, views.detail, name=‘detail’),
]

Django 使用正则表达式来匹配用户访问的网址。这里 r’^post/(?P<pk>[0-9]+)/$’ 整个正则表达式刚好匹配我们上面定义的 URL 规则。这条正则表达式的含义是,以 post/ 开头,后跟一个至少一位数的数字,并且以 / 符号结尾,如 post/1/、 post/255/ 等都是符合规则的,[0-9]+ 表示一位或者多位数。此外这里 (?P<pk>[0-9]+) 表示命名捕获组,其作用是从用户访问的 URL 里把括号内匹配的字符串捕获并作为关键字参数传给其对应的视图函数 detail。比如当用户访问 post/255/ 时(注意 Django 并不关心域名,而只关心去掉域名后的相对 URL),被括起来的部分 (?P<pk>[0-9]+) 匹配 255,那么这个 255 会在调用视图函数 detail 时被传递进去,实际上视图函数的调用就是这个样子:detail(request, pk=255)。我们这里必须从 URL 里捕获文章的 id,因为只有这样我们才能知道用户访问的究竟是哪篇文章。

此外我们通过 app_name=‘blog’ 告诉 Django 这个 urls.py 模块是属于 blog 应用的,这种技术叫做视图函数命名空间。我们看到 blog\urls.py 目前有两个视图函数,并且通过 name 属性给这些视图函数取了个别名,分别是 index、detail。但是一个复杂的 Django 项目可能不止这些视图函数,例如一些第三方应用中也可能有叫 index、detail 的视图函数,那么怎么把它们区分开来,防止冲突呢?方法就是通过 app_name 来指定命名空间,命名空间具体如何使用将在下面介绍。

为了方便地生成上述的 URL,我们在 Post 类里定义一个 get_absolute_url方法,注意 Post 本身是一个 Python 类,在类中我们是可以定义任何方法的。

blog/models.py

from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse

class Post(models.Model):

def <span class="token function">__str__</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token keyword">return</span> self<span class="token punctuation">.</span>title# 自定义 get_absolute_url 方法
# 记得从 django<span class="token punctuation">.</span>urls 中导入 reverse 函数
def <span class="token function">get_absolute_url</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span><span class="token keyword">return</span> <span class="token function">reverse</span><span class="token punctuation">(</span><span class="token string">'blog:detail'</span><span class="token punctuation">,</span> kwargs<span class="token operator">=</span><span class="token punctuation">{</span><span class="token string">'pk'</span><span class="token punctuation">:</span> self<span class="token punctuation">.</span>pk<span class="token punctuation">}</span><span class="token punctuation">)</span></pre><p>注意到 URL 配置中的&nbsp;<code>url(r'^post/(?P&lt;pk&gt;[0-9]+)/$', views.detail, name='detail')</code>&nbsp;,我们设定的&nbsp;<code>name='detail'</code>&nbsp;在这里派上了用场。看到这个&nbsp;<code>reverse</code>&nbsp;函数,它的第一个参数的值是&nbsp;<code>'blog:detail'</code>,意思是 blog 应用下的&nbsp;<code>name=detail</code>&nbsp;的函数,由于我们在上面通过&nbsp;<code>app_name = 'blog'</code>&nbsp;告诉了 Django 这个 URL 模块是属于 blog 应用的,因此 Django 能够顺利地找到 blog 应用下 name 为 detail 的视图函数,于是&nbsp;<code>reverse</code>&nbsp;函数会去解析这个视图函数对应的 URL,我们这里 detail 对应的规则就是&nbsp;<code>post/(?P&lt;pk&gt;[0-9]+)/</code>这个正则表达式,而正则表达式部分会被后面传入的参数&nbsp;<code>pk</code>&nbsp;替换,所以,如果&nbsp;<code>Post</code>&nbsp;的 id(或者 pk,这里 pk 和 id 是等价的) 是 255 的话,那么&nbsp;<code>get_absolute_url</code>&nbsp;函数返回的就是 /post/255/ ,这样 Post 自己就生成了自己的 URL。</p><h3>8.2.编写detail视图函数</h3><p>&nbsp;接下来就是实现我们的&nbsp;<code>detail</code>&nbsp;视图函数了:</p><pre class="prism-token token  language-javascript"># blog<span class="token operator">/</span>views<span class="token punctuation">.</span>py

from django.shortcuts import render,get_object_or_404
from . models import Post

def index(request):
post_list = Post.objects.all().order_by(’-created_time’)
return render(request,‘blog/index.html’,{‘post_list’:post_list})

def detail(request,pk):
post = get_object_or_404(Post,pk=pk)
return render(request,‘blog/detail.html’,{‘post’:post})

视图函数很简单,它根据我们从 URL 捕获的文章 id(也就是 pk,这里 pk 和 id 是等价的)获取数据库中文章 id 为该值的记录,然后传递给模板。注意这里我们用到了从 django.shortcuts 模块导入的 get_object_or_404 方法,其作用就是当传入的 pk 对应的 Post 在数据库存在时,就返回对应的 post,如果不存在,就给用户返回一个 404 错误,表明用户请求的文章不存在。

8.3.编写详情页模板

接下来就是书写模板文件,从下载的博客模板中把 single.html 拷贝到 templates\blog 目录下(和 index.html 在同一级目录),然后改名为 detail.html。

在 index 页面博客文章列表的标题和继续阅读按钮写上超链接跳转的链接,即文章 post 对应的详情页的 URL,让用户点击后可以跳转到 detail 页面:

 <article class=“post post-{{ post.pk }}”>
<header class=“entry-header”>
<h1 class=“entry-title”>
<a href="{{ post.get_absolute_url }}">{{ 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="{{ post.get_absolute_url }}" class=“more-link”>继续阅读 <span class=“meta-nav”></span></a>
</div>
</div>
</article>

这里我们修改两个地方,第一个是文章标题处:

<h1 class=“entry-title”>
<a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
</h1>

我们把 a 标签的 href 属性的值改成了 {{ post.get_absolute_url }}。回顾一下模板变量的用法,由于 get_absolute_url 这个方法(我们定义在 Post 类中的)返回的是 post 对应的 URL,因此这里 {{ post.get_absolute_url }} 最终会被替换成该 post 自身的 URL。

同样,第二处修改的是继续阅读按钮的链接:

<a href="{{ post.get_absolute_url }}" class=“more-link”>继续阅读 <span class=“meta-nav”></span>
</a>

这样当我们点击首页文章的标题或者继续阅读按钮后就会跳转到该篇文章对应的详情页面了。然而如果你尝试跳转到详情页后,你会发现样式是乱的。这在 真正的 Django 博客首页 时讲过,由于我们是直接复制的模板,还没有正确地处理静态文件。我们可以按照介绍过的方法修改静态文件的引入路径,但很快你会发现在任何页面都是需要引入这些静态文件,如果每个页面都要修改会很麻烦,而且代码都是重复的。下面就介绍 Django 模板继承的方法来帮我们消除这些重复操作。

8.4.模板继承

我们看到 index.html 文件和 detail.html 文件除了 main 标签包裹的部分不同外,其它地方都是相同的,我们可以把相同的部分抽取出来,放到 base.html 里。首先在 templates\ 目录下新建一个 base.html 文件

把 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>

这里 block 也是一个模板标签,其作用是占位。比如这里的 {% block main %}{% endblock main %} 是一个占位框,main 是我们给这个 block 取的名字。下面我们会看到 block 标签的作用。同时我们也在 aside 标签下加了一个 {% block toc %}{% endblock toc %} 占位框,因为 detail.html 中 aside 标签下会多一个目录栏。当 {% block toc %}{% endblock toc %} 中没有任何内容时,{% block toc %}{% endblock toc %} 在模板中不会显示。但当其中有内容是,模板就会显示 block 中的内容。

在 index.html 里,我们在文件最顶部使用 {% extends ‘base.html’ %} 继承 base.html,这样就把 base.html 里的代码继承了过来,另外在 {% block main %}{% endblock main %} 包裹的地方填上 index 页面应该显示的内容:

templates/blog/index.html

{% extends ‘blog/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 %}

这样 base.html 里的代码加上 {% block main %}{% endblock main %} 里的代码就和最开始 index.html 里的代码一样了。这就是模板继承的作用,公共部分的代码放在 base.html 里,而其它页面不同的部分通过替换 {% block main %}{% endblock main %} 占位标签里的内容即可。

如果你对这种模板继承还是有点糊涂,可以把这种继承和 Python 中类的继承类比。base.html 就是父类,index.html 就是子类。index.html 继承了 base.html 中的全部内容,同时它自身还有一些内容,这些内容就通过 “覆写” {% block main %}{% endblock main %}(把 block 看做是父类的属性)的内容添加即可。

detail 页面处理起来就简单了,同样继承 base.html ,在 {% block main %}{% endblock main %} 里填充 detail.html 页面应该显示的内容,以及在 {% block toc %}{% endblock toc %} 中填写 base.html 中没有的目录部分的内容。不过目前的目录只是占位数据,我们在以后会实现如何从文章中自动摘取目录。

templates/blog/detail.html

{% extends ‘blog/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 %}

修改 article 标签下的一些内容,让其显示文章的实际数据:

<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>

再次从首页点击一篇文章的标题或者继续阅读按钮跳转到详情页面,可以看到预期效果了!

 九、支持Markdown语法和代码高亮

 为了让博客文章具有良好的排版,显示更加丰富的格式,我们使用 Markdown 语法来书写我们的博文。Markdown 是一种 HTML 文本标记语言,只要遵循它约定的语法格式,Markdown 的渲染器就能够把我们写的文章转换为标准的 HTML 文档,从而让我们的文章呈现更加丰富的格式,例如标题、列表、代码块等等 HTML 元素。由于 Markdown 语法简单直观,不用超过 5 分钟就可以掌握常用的标记语法,因此大家青睐使用 Markdown 书写 HTML 文档。下面让我们的博客也支持使用 Markdown 书写。

 9.1.在detail视图中渲染Markdown

首先安装mrakdown模块: pip instal markdownl

将 Markdown 格式的文本渲染成 HTML 文本非常简单,只需调用这个库的 markdown 方法即可。我们书写的博客文章内容存在 Post 的 body 属性里,回到我们的详情页视图函数,对 post 的 body 的值做一下渲染,把 Markdown 文本转为 HTML 文本再传递给模板:

blog/views.py

import markdown
from django.shortcuts import render, get_object_or_404
from .models import Post

def detail(request, pk):
post = get_object_or_404(Post, pk=pk)
# 记得在顶部引入 markdown 模块
post.body = markdown.markdown(post.body,
extensions=[
‘markdown.extensions.extra’,
‘markdown.extensions.codehilite’,
‘markdown.extensions.toc’,
])
return render(request, ‘blog/detail.html’, {‘post’: post})

这样我们在模板中展示 {{ post.body }} 的时候,就不再是原始的 Markdown 文本了,而是渲染过后的 HTML 文本。注意这里我们给 markdown 渲染函数传递了额外的参数 extensions,它是对 Markdown 语法的拓展,这里我们使用了三个拓展,分别是 extra、codehilite、toc。extra 本身包含很多拓展,而 codehilite 是语法高亮拓展,这为我们后面的实现代码高亮功能提供基础,而 toc 则允许我们自动生成目录(在以后会介绍)。

来测试一下效果,进入后台,这次我们发布一篇用 Markdown 语法写的测试文章看看,你可以使用以下的 Markdown 测试代码进行测试,也可以自己书写你喜欢的 Markdown 文本。一下是我学习markdown的参考资料地址

Markdown入门指南

Markdown语法说明

python markdown 语法

# 一级标题

二级标题

三级标题

- 列表项1
- 列表项2
- 列表项3

> 这是一段引用
~~~~python
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})
~~~~

safe标签

我们在发布的文章详情页没有看到预期的效果,而是类似于一堆乱码一样的 HTML 标签,这些标签本应该在浏览器显示它本身的格式,但是 Django 出于安全方面的考虑,任何的 HTML 代码在 Django 的模板中都会被转义(即显示原始的 HTML 代码,而不是经浏览器渲染后的格式)。为了解除转义,只需在模板标签使用 safe 过滤器即可,告诉 Django,这段文本是安全的,你什么也不用做。在模板中找到展示博客文章主体的 {{ post.body }} 部分,为其加上 safe 过滤器,{{ post.body|safe }},大功告成,这下看到预期效果了。

9.2.代码高亮

程序员写博客免不了要插入一些代码,Markdown 的语法使我们容易地书写代码块,但是目前来说,显示的代码块里的代码没有任何颜色,很不美观,也难以阅读,要是能够像我们的编辑器里一样让代码高亮就好了。虽然我们在渲染时使用了 codehilite 拓展,但这只是实现代码高亮的第一步,还需要简单的几步才能达到我们的最终目的。

 安装Pygments

 首先安装pygments模块,pip install pygments

搞定了,虽然我们除了安装了一下 Pygments 什么也没做,但 Markdown 使用 Pygments 在后台为我们做了很多事。如果你打开博客详情页,找到一段代码段,在浏览器查看这段代码段的 HTML 源代码,可以发现 Pygments 的工作原理是把代码切分成一个个单词,然后为这些单词添加 css 样式,不同的词应用不同的样式,这样就实现了代码颜色的区分,即高亮了语法。为此,还差最后一步,引入一个样式文件来给这些被添加了样式的单词定义颜色。

引入样式文件

在项目的 blog\static\blog\css\highlights\ 目录下应该能看到很多 .css 样式文件,这些文件是用来提供代码高亮样式的。选择一个你喜欢的样式文件,在 base.html 引入即可(别忘了使用 static 模板标签)。比如我比较喜欢 github.css 的样式,那么引入这个文件:

templates/base.html

<link rel=“stylesheet” href="{% static ‘blog/css/highlights/github.css’ %}">

好了,看看效果。

 下一篇: Django搭建博客网站(2)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_794048.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是最好的网站学习如何使用Linux系统?

2019独角兽企业重金招聘Python工程师标准>>> 其实不是很难使用Linux&#xff0c;由于有关系统的大量技术信息加上现在网上的信息鱼龙混杂&#xff0c;各种信息让我们分不清是真是假&#xff0c;您可能会被愚弄&#xff0c;但毕竟真的不是那么困难。为了给你一个开始…

Apache设置禁止访问网站目录

Apache默认在当前目录下没有index.html入口就会显示网站根目录&#xff0c;让网站目录文件都暴露在外面&#xff0c;是一件非常危险的事&#xff0c;例如&#xff1a;数据库密码泄露&#xff0c;隐藏页面暴露等严重安全问题&#xff01; 例如&#xff0c;访问米扑网站根目录: h…

米扑科技的开源项目:sitemap-php 自动生成网站地图

米扑科技旗下的产品&#xff0c;近期正在做SEO网站优化&#xff0c;其中子需求之一是调研实现了网站地图&#xff08;sitemap.xml&#xff09; 封装简化了许多功能模块&#xff0c;现在分享出来&#xff0c;源代码可在Github上下载&#xff0c;有简单的示例。Github 开源网址&a…

十大免费SSL证书:网站免费添加HTTPS加密

SSL证书&#xff0c;用于加密HTTP协议&#xff0c;也就是HTTPS。随着淘宝、百度等网站纷纷实现全站Https加密访问&#xff0c;搜索引擎对于Https更加友好&#xff0c;加上互联网上越来越多的人重视隐私安全&#xff0c;站长们给网站添加SSL证书似乎成为了一种趋势。 给自己的网…

十大免费SSL证书:网站免费添加HTTPS加密

SSL证书&#xff0c;用于加密HTTP协议&#xff0c;也就是HTTPS。随着淘宝、百度等网站纷纷实现全站Https加密访问&#xff0c;搜索引擎对于Https更加友好&#xff0c;加上互联网上越来越多的人重视隐私安全&#xff0c;站长们给网站添加SSL证书似乎成为了一种趋势。 给自己的网…

网站域名、备案、七牛云图床重新搭建与博客整理

最近碰上了一个问题&#xff0c;七牛云开始回收测试域名&#xff0c;结果导致我的图床有些失灵、一些软件资源都无法下载。其实这个问题在前一段不蒜子统计修改的博客里已经有兆头了。不过当时没注意。最后还是被逼得只能购买了域名&#xff0c;各类备案&#xff0c;重新搭建图…

python模拟用户浏览器登陆网站

2019独角兽企业重金招聘Python工程师标准>>> 【前置条件】 Python 2.7.13 |CentOS release 6.5 方案一&#xff1a;使用urllib2库 首先使用自己的账号和密码在浏览器登录&#xff0c;然后通过抓包拿到cookie&#xff0c;再将cookie放到请求之中发送请求即可&#xf…

LAMP架构部署和动态网站环境的配置

实验环境&#xff1a; 操作系统&#xff1a;centos 7.5 服务器IP&#xff1a;192.168.10.5 运行用户&#xff1a;root 连接工具&#xff1a;xshell工具 web环境&#xff1a;Linuxapachephpmariadb&#xff08;LAMP架构&#xff09; 大型动态应用系统平台主要是针对于大流量、高…

10个帮助你优化网站的 .htaccess 技巧

“.htaccess”文件旺旺被网页设计师们忽略。假如你还不知道什么是htaccess的话&#xff0c;你可以去查一下wikipedia。它是目录级别的配置文件&#xff0c;有常用的网页服务器支持这种配置&#xff0c;例如Apache。下面我将列出10条有用的.htaccess配置技巧。 1. 反盗链 那些盗…

emlog rss.php,emlog伪静态设置-我的建站第一天 - 慕轲博客-建立自己的个人自媒体博客...

由于我住的地方网络比较卡&#xff0c;大的引擎比较卡&#xff0c;打开慢&#xff0c;果断选择emlog这款cms建了个个人博客&#xff0c;那么刚用问题就来了&#xff0c;什么问题呢&#xff1f;建站的人都知道&#xff0c;网站一般都要做静态或者伪静态的&#xff0c;emlog不支持…

计算机组成原理寄存器初始化,8255的寻址与初始化-计算机组成原理与汇编语言-电子发烧友网站...

6.5.18255的寻址及连接使用8255占外设编址的4个地址,即A口、B口、C口和控制寄存器各占一个外设接口地址。对同一个地址分别可以进行读写操作。根据这种寻址结构,可以方便地将8255连接到系统总线上,如图6.27上所示。由图6.27可见,8255与8088总线连接是比较容易的。只是图中为了简…

sql服务器网站备份工具,使用 SQL Server 工具备份服务器场 (Search Server 2008)

使用 SQL Server 工具备份服务器场 (Search Server 2008)06/16/2015本文内容更新时间: 2008年7月应用到: Microsoft Search Server 2008上一次修改主题&#xff1a; 2009-12-02草稿内容 本文是过去针对此产品的早期版本发布的&#xff0c;现在未经任何更改又针对此版本进行发布…

easyui获取图片路径_初学Python-只需4步,爬取网站图片(附py文件)

很多人学习Python很重要的一个原因是&#xff0c;可以很简单的把一个网站的数据爬下来。尤其是做我们这一行&#xff0c;产品经理&#xff0c;电商行业。领导&#xff1a;弄一个买卖游戏周边商品的交易APP出来。我&#xff1a;行&#xff0c;那我们卖什么呀&#xff1f;领导&am…

搜索引擎制作_搜索引擎优化方法_网站制作流程:网站建设7个基本流程!

发布时间:2020-12-08 10:12:16从目前来看&#xff0c;每天都会有大量的初创企业诞生&#xff0c;作为企业主而言&#xff0c;第一时间想到的事情&#xff0c;就是建立一个网站&#xff0c;但由于初创企业&#xff0c;并没有专业相关的网站制作人员&#xff0c;往往是寻求网站建…

apache服务器配置tls_Linux 系统管理及服务配置实战-第43章 APACHE网站服务器构建I...

Apache服务器1 简介1.1 前言UI的转变&#xff1a;B/S架构1.2 名词HTMLHyperText Markup Language 网页使用HTML,PHP,JAVA语言格式书写的文件。主页网页中呈现用户的第一个页面。URLUniform Resource Locator 统一资源定位符1.3 Web ServerNginx&#xff08;Tengine&#xff09;…

php网站无法预缆,解决wp-super-cache无法(预)缓存问题

突然发现WP-SUPER-CACHE无法预缓存&#xff0c;点击【立即加载预缓存】后没有任何效果&#xff0c;并且垃圾回收定时器也失效了&#xff0c;缓存文件全是几天前的&#xff0c;感觉很奇怪&#xff01;闲下来的时候&#xff0c;突然想起前些天对博客做的一些优化&#xff0c;于是…

PPTV网站强化移动视频高端内容

随着移动互联网用户规模的快速增长&#xff0c;移动终端的商业价值也在日益显现。聚力传媒(PPTV.com&#xff0c;以下简称“PPTV网站”)近日在移动终端动作频频&#xff0c;继iPad深度专刊改版后再次发力高端人群&#xff0c;率先行业打造移动终端专业财经视频内容&#xff0c;…

阿里云部署Java网站和微信开发调试心得技巧

阿里云部署Java网站和微信开发调试心得技巧(上) 阿里云部署Java网站和微信开发调试心得技巧&#xff08;上&#xff09; 本篇手记旨在帮助大家从0开始&#xff1a; 申请阿里云服务器搭建出程序的执行环境在服务器上发布并运行自己的web project域名解析微信测试号的申请与连接…

探究 Stack Overflow 网站的热点问题排名算法

引入&#xff1a;几乎所有搞开发的人都对Stack Overflow不陌生&#xff0c;它是一个专门的Q&A类型的网站http://dbanotes.net/startup/stack_overflow_success.html &#xff08;类似这种网站的例子还有很多&#xff0c;比如新浪问问&#xff0c;百度搜搜等&#xff09;你如…

为什么别人做小说网站轻易IP上万,自己做甚至不收录

2019独角兽企业重金招聘Python工程师标准>>> 最近与博客联盟(blogunion.org)的老曾好友聊了下关于小说网站&#xff0c;听说小说网站轻易IP上万&#xff0c;而自己做甚至不收录(当然不是老曾本人的)&#xff0c;感到十分困惑。于是深度了解下&#xff0c;后来发现&a…