欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python+Django搭建自己的blog網(wǎng)站

 更新時(shí)間:2018年03月13日 08:52:41   作者:zhang_derek  
一直有種想要搭建自己博客系統(tǒng)打沖動(dòng),可惜需要前端知識(shí)較多,而且還要安裝一系列軟件并配置(windows平臺(tái)),后來(lái)偶然發(fā)現(xiàn)了Django這個(gè)神器,恰巧剛學(xué)過(guò)python,于是就又裝了ubuntu(安裝各種軟件配置都方便),折騰了半天,終于搭建起來(lái)自己的第一個(gè)簡(jiǎn)易博客。

一、前言

1.1.環(huán)境

python版本:3.6

Django版本:1.11.6

1.2.預(yù)覽效果

最終搭建的blog的樣子,基本上滿足需求了。框架搭好了,至于CSS,可以根據(jù)自己喜好隨意搭配。

二、建立博客應(yīng)用

2.1.建立項(xiàng)目和應(yīng)用

創(chuàng)建工程blogproject

python manage.py startproject blogproject
創(chuàng)建blog應(yīng)用
python manage.py startpapp blog
打開(kāi) blogproject\ 目錄下的 settings.py 文件,找到 INSTALLED_APPS 設(shè)置項(xiàng),將 blog 應(yīng)用添加進(jìn)去。
INSTALLED_APPS = [
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'blog',
]

2.2.目錄結(jié)構(gòu)

三、創(chuàng)建blog的數(shù)據(jù)庫(kù)模型

3.1.設(shè)計(jì)博客的數(shù)據(jù)庫(kù)表結(jié)構(gòu)

博客最主要的功能就是展示我們寫(xiě)的文章,它需要從某個(gè)地方獲取博客文章數(shù)據(jù)才能把文章展示出來(lái),通常來(lái)說(shuō)這個(gè)地方就是數(shù)據(jù)庫(kù)。我們把寫(xiě)好的文章永久地保存在數(shù)據(jù)庫(kù)里,當(dāng)用戶訪問(wèn)我們的博客時(shí),Django 就去數(shù)據(jù)庫(kù)里把這些數(shù)據(jù)取出來(lái)展現(xiàn)給用戶。

博客的文章應(yīng)該含有標(biāo)題、正文、作者、發(fā)表時(shí)間等數(shù)據(jù)。一個(gè)更加現(xiàn)代化的博客文章還希望它有分類、標(biāo)簽、評(píng)論等。為了更好地存儲(chǔ)這些數(shù)據(jù),我們需要合理地組織數(shù)據(jù)庫(kù)的表結(jié)構(gòu)。

 我們的博客初級(jí)版本主要包含博客文章,文章會(huì)有分類以及標(biāo)簽。一篇文章只能有一個(gè)分類,但可以打上很多標(biāo)簽。我們把分類和標(biāo)簽做成單獨(dú)的數(shù)據(jù)庫(kù)表,再把文章和分類、標(biāo)簽關(guān)聯(lián)起來(lái)。下面分別是分類和標(biāo)簽的數(shù)據(jù)庫(kù)表:

 分類id    分類名

     1         python

     2         Django

標(biāo)簽id      標(biāo)簽名

     1          python學(xué)習(xí)

     2          Django學(xué)習(xí)

3.2.編寫(xiě)博客模型代碼

 分類數(shù)據(jù)庫(kù)表:

# blog/models.py

from django.db import models

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

Category 就是一個(gè)標(biāo)準(zhǔn)的 Python 類,它繼承了 models.Model 類,類名為 Category 。Category 類有一個(gè)屬性 name,它是 models.CharField 的一個(gè)實(shí)例。

我們需要 3 個(gè)表格:文章(Post)、分類(Category)以及標(biāo)簽(Tag),下面就來(lái)分別編寫(xiě)它們對(duì)應(yīng)的 Python 類。模型的代碼通常寫(xiě)在相關(guān)應(yīng)用的 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 只需要一個(gè)簡(jiǎn)單的分類名 name 就可以了。
 CharField 指定了分類名 name 的數(shù)據(jù)類型,CharField 是字符型,
 CharField 的 max_length 參數(shù)指定其最大長(zhǎng)度,超過(guò)這個(gè)長(zhǎng)度的分類名就不能被存入數(shù)據(jù)庫(kù)。
 '''
 name = models.CharField(max_length=100)

class Tag(models.Model):
 '''標(biāo)簽'''

 name = models.CharField(max_length=100)

class Post(models.Model):
 '''文章'''

 # 文章標(biāo)題
 title = models.CharField(max_length=70)

 # 文章正文,我們使用了 TextField。
 # 存儲(chǔ)比較短的字符串可以使用 CharField,但對(duì)于文章的正文來(lái)說(shuō)可能會(huì)是一大段文本,因此使用 TextField 來(lái)存儲(chǔ)大段文本。
 body = models.TextField()

 # 這兩個(gè)列分別表示文章的創(chuàng)建時(shí)間和最后一次修改時(shí)間,存儲(chǔ)時(shí)間的字段用 DateTimeField 類型。
 created_time = models.DateTimeField()
 modified_time = models.DateTimeField()

 # 文章摘要,可以沒(méi)有文章摘要,但默認(rèn)情況下 CharField 要求我們必須存入數(shù)據(jù),否則就會(huì)報(bào)錯(cuò)。
 # 指定 CharField 的 blank=True 參數(shù)值后就可以允許空值了。
 excerpt = models.CharField(max_length=200,blank=True)

 # 我們?cè)谶@里把文章對(duì)應(yīng)的數(shù)據(jù)庫(kù)表和分類、標(biāo)簽對(duì)應(yīng)的數(shù)據(jù)庫(kù)表關(guān)聯(lián)了起來(lái),但是關(guān)聯(lián)形式稍微有點(diǎn)不同。
 # 我們規(guī)定一篇文章只能對(duì)應(yīng)一個(gè)分類,但是一個(gè)分類下可以有多篇文章,所以我們使用的是 ForeignKey,即一對(duì)多的關(guān)聯(lián)關(guān)系。
 # 而對(duì)于標(biāo)簽來(lái)說(shuō),一篇文章可以有多個(gè)標(biāo)簽,同一個(gè)標(biāo)簽下也可能有多篇文章,所以我們使用 ManyToManyField,表明這是多對(duì)多的關(guān)聯(lián)關(guān)系。
 # 同時(shí)我們規(guī)定文章可以沒(méi)有標(biāo)簽,因此為標(biāo)簽 tags 指定了 blank=True。
 category = models.ForeignKey(Category,on_delete=models.CASCADE)
 tags = models.ManyToManyField(Tag,blank=True)

 # 文章作者,這里 User 是從 django.contrib.auth.models 導(dǎo)入的。
 # django.contrib.auth 是 Django 內(nèi)置的應(yīng)用,專門(mén)用于處理網(wǎng)站用戶的注冊(cè)、登錄等流程,User 是 Django 為我們已經(jīng)寫(xiě)好的用戶模型。
 # 這里我們通過(guò) ForeignKey 把文章和 User 關(guān)聯(lián)了起來(lái)。
 # 因?yàn)槲覀円?guī)定一篇文章只能有一個(gè)作者,而一個(gè)作者可能會(huì)寫(xiě)多篇文章,因此這是一對(duì)多的關(guān)聯(lián)關(guān)系,和 Category 類似。
 author = models.ForeignKey(User,on_delete=models.CASCADE)

四、遷移數(shù)據(jù)庫(kù)

4.1.設(shè)置數(shù)據(jù)庫(kù)為Mysql

更改setting.py默認(rèn)配置

# DATABASES = {
#  'default': {
#   'ENGINE': 'django.db.backends.sqlite3',
#   'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
#  }
# }

DATABASES = {
 'default': {
  'ENGINE': 'django.db.backends.mysql',
  'NAME': 'django',  #數(shù)據(jù)庫(kù)名字
  'USER': 'root',   #賬號(hào)
  'PASSWORD': '123456',  #密碼
  'HOST': '127.0.0.1', #IP
  'PORT': '3306',     #端口
 }
}
導(dǎo)入Pymysql
# blog/__init__.py

import pymysql
pymysql.install_as_MySQLdb()

4.2.遷移數(shù)據(jù)庫(kù)

分別運(yùn)行下面兩條命令

python manage.py makemigrations 
python manage.py migrate
當(dāng)我們執(zhí)行了 python manage.py makemigrations 后,Django 在 blog 應(yīng)用的 migrations\ 目錄下生成了一個(gè) 0001_initial.py 文件,這個(gè)文件是 Django 用來(lái)記錄我們對(duì)模型做了哪些修改的文件。目前來(lái)說(shuō),我們?cè)?models.py 文件里創(chuàng)建了 3 個(gè)模型類,Django 把這些變化記錄在了 0001_initial.py 里。

不過(guò)此時(shí)還只是告訴了 Django 我們做了哪些改變,為了讓 Django 真正地為我們創(chuàng)建數(shù)據(jù)庫(kù)表,接下來(lái)又執(zhí)行了 python manage.py migrate 命令。Django 通過(guò)檢測(cè)應(yīng)用中 migrations\ 目錄下的文件,得知我們對(duì)數(shù)據(jù)庫(kù)做了哪些操作,然后它把這些操作翻譯成數(shù)據(jù)庫(kù)操作語(yǔ)言,從而把這些操作作用于真正的數(shù)據(jù)庫(kù)。

你可以看到命令的輸出除了 Applying blog.0001_initial... OK 外,Django 還對(duì)其它文件做了操作。這是因?yàn)槌宋覀冏约航⒌?blog 應(yīng)用外,Django 自身還內(nèi)置了很多應(yīng)用,這些應(yīng)用本身也是需要存儲(chǔ)數(shù)據(jù)的??梢栽?settings.py 的 INSTALLED_APP 設(shè)置里看到這些應(yīng)用,當(dāng)然我們目前不必關(guān)心這些。

#blogproject/settings.py

INSTALLED_APPS = [
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'blog',
]
運(yùn)行下面的命令將看到輸出了經(jīng) Django 翻譯后的數(shù)據(jù)庫(kù)表創(chuàng)建語(yǔ)句,這有助于你理解 Django ORM 的工作機(jī)制。
python manage.py sqlmigrate blog 0001

4.3.存數(shù)據(jù)

打開(kāi)一個(gè)交互式命令行

python manage.py shell
首先我們來(lái)創(chuàng)建一個(gè)分類和一個(gè)標(biāo)簽:

我們首先導(dǎo)入 3 個(gè)之前寫(xiě)好的模型類,然后實(shí)例化了一個(gè) Category 類和一個(gè) Tag 類,為他們的屬性 name 賦了值。為了讓 Django 把這些數(shù)據(jù)保存進(jìn)數(shù)據(jù)庫(kù),調(diào)用實(shí)例的 save方法即可。

創(chuàng)建文章之前,我們需要先創(chuàng)建一個(gè) User,用于指定文章的作者。創(chuàng)建 User 的命令 Django 已經(jīng)幫我們寫(xiě)好了,依然是通過(guò) manage.py 來(lái)運(yùn)行。首先exit()退出命令交互欄,運(yùn)行 python manage.py createsuperuser 命令并根據(jù)提示創(chuàng)建用戶:用戶名,郵箱,密碼

再次運(yùn)行 python manage.py shell 進(jìn)入 Python 命令交互欄,開(kāi)始創(chuàng)建文章:

由于我們重啟了 shell,因此需要重新導(dǎo)入了 Category、Tag、Post 以及 User。我們還導(dǎo)入了一個(gè) Django 提供的輔助模塊 timezone,這是因?yàn)槲覀冃枰{(diào)用它的 now() 方法為 created_time 和 modified_time 指定時(shí)間,容易理解 now 方法返回當(dāng)前時(shí)間。然后我們根據(jù)用戶名和分類名,通過(guò) get 方法取出了存在數(shù)據(jù)庫(kù)中的 User 和 Category(取數(shù)據(jù)的方法將在下面介紹)。接著我們?yōu)槲恼轮付?nbsp;title、body 、created_time、modified_time值,并把它和前面創(chuàng)建的 Category 以及 User 關(guān)聯(lián)了起來(lái)。允許為空 excerpttags 我們就沒(méi)有為它們指定值了。

4.4.取數(shù)據(jù)

 數(shù)據(jù)已經(jīng)存入數(shù)據(jù)庫(kù)了,現(xiàn)在要把它們?nèi)〕鰜?lái)看看:

 

objects 是我們的模型管理器,它為我們提供一系列從數(shù)據(jù)庫(kù)中取數(shù)據(jù)方法,這里我們使用了 all 方法,表示我們要把對(duì)應(yīng)的數(shù)據(jù)全部取出來(lái)??梢钥吹?nbsp;all 方法都返回了數(shù)據(jù),這些數(shù)據(jù)應(yīng)該是我們之前存進(jìn)去的,但是顯示的字符串有點(diǎn)奇怪,無(wú)法看出究竟是不是我們之前存入的數(shù)據(jù)。為了讓顯示出來(lái)的數(shù)據(jù)更加人性化一點(diǎn),我們?yōu)?3 個(gè)模型分別增加一個(gè) __str__ 方法:

# blog/models.py

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

class Category(models.Model):
 '''
 Django 要求模型必須繼承 models.Model 類。
 Category 只需要一個(gè)簡(jiǎn)單的分類名 name 就可以了。
 CharField 指定了分類名 name 的數(shù)據(jù)類型,CharField 是字符型,
 CharField 的 max_length 參數(shù)指定其最大長(zhǎng)度,超過(guò)這個(gè)長(zhǎng)度的分類名就不能被存入數(shù)據(jù)庫(kù)。
 '''
 name = models.CharField(max_length=100)

 def __str__(self):
  return self.name

class Tag(models.Model):
 '''標(biāo)簽'''

 name = models.CharField(max_length=100)

 def __str__(self):
  return self.name

class Post(models.Model):
 '''文章'''

 # 文章標(biāo)題
 title = models.CharField(max_length=70)

 # 文章正文,我們使用了 TextField。
 # 存儲(chǔ)比較短的字符串可以使用 CharField,但對(duì)于文章的正文來(lái)說(shuō)可能會(huì)是一大段文本,因此使用 TextField 來(lái)存儲(chǔ)大段文本。
 body = models.TextField()

 # 這兩個(gè)列分別表示文章的創(chuàng)建時(shí)間和最后一次修改時(shí)間,存儲(chǔ)時(shí)間的字段用 DateTimeField 類型。
 created_time = models.DateTimeField()
 modified_time = models.DateTimeField()

 # 文章摘要,可以沒(méi)有文章摘要,但默認(rèn)情況下 CharField 要求我們必須存入數(shù)據(jù),否則就會(huì)報(bào)錯(cuò)。
 # 指定 CharField 的 blank=True 參數(shù)值后就可以允許空值了。
 excerpt = models.CharField(max_length=200,blank=True)

 # 我們?cè)谶@里把文章對(duì)應(yīng)的數(shù)據(jù)庫(kù)表和分類、標(biāo)簽對(duì)應(yīng)的數(shù)據(jù)庫(kù)表關(guān)聯(lián)了起來(lái),但是關(guān)聯(lián)形式稍微有點(diǎn)不同。
 # 我們規(guī)定一篇文章只能對(duì)應(yīng)一個(gè)分類,但是一個(gè)分類下可以有多篇文章,所以我們使用的是 ForeignKey,即一對(duì)多的關(guān)聯(lián)關(guān)系。
 # 而對(duì)于標(biāo)簽來(lái)說(shuō),一篇文章可以有多個(gè)標(biāo)簽,同一個(gè)標(biāo)簽下也可能有多篇文章,所以我們使用 ManyToManyField,表明這是多對(duì)多的關(guān)聯(lián)關(guān)系。
 # 同時(shí)我們規(guī)定文章可以沒(méi)有標(biāo)簽,因此為標(biāo)簽 tags 指定了 blank=True。
 category = models.ForeignKey(Category,on_delete=models.CASCADE)
 tags = models.ManyToManyField(Tag,blank=True)

 # 文章作者,這里 User 是從 django.contrib.auth.models 導(dǎo)入的。
 # django.contrib.auth 是 Django 內(nèi)置的應(yīng)用,專門(mén)用于處理網(wǎng)站用戶的注冊(cè)、登錄等流程,User 是 Django 為我們已經(jīng)寫(xiě)好的用戶模型。
 # 這里我們通過(guò) ForeignKey 把文章和 User 關(guān)聯(lián)了起來(lái)。
 # 因?yàn)槲覀円?guī)定一篇文章只能有一個(gè)作者,而一個(gè)作者可能會(huì)寫(xiě)多篇文章,因此這是一對(duì)多的關(guān)聯(lián)關(guān)系,和 Category 類似。
 author = models.ForeignKey(User,on_delete=models.CASCADE)

 def __str__(self):
  return self.title
__str__

定義好 __str__ 方法后,解釋器顯示的內(nèi)容將會(huì)是 __str__ 方法返回的內(nèi)容。這里 Category 返回分類名 name ,Tag 返回標(biāo)簽名,而 Post 返回它的 title。

 exit() 退出 Shell,再重新運(yùn)行 python manage.py shell 進(jìn)入 Shell。

 

可以看到返回的是我們之前存入的數(shù)據(jù)。

此外我們?cè)趧?chuàng)建文章時(shí)提到了通過(guò) get 方法來(lái)獲取數(shù)據(jù),這里 all 方法和 get 方法的區(qū)別是:all 方法返回全部數(shù)據(jù),是一個(gè)類似于列表的數(shù)據(jù)結(jié)構(gòu)(QuerySet);而 get 返回一條記錄數(shù)據(jù),如有多條記錄或者沒(méi)有記錄,get 方法均會(huì)拋出相應(yīng)異常。

 五、博客首頁(yè)視圖

5.1.Django處理HTTP請(qǐng)求

Web 應(yīng)用的交互過(guò)程其實(shí)就是 HTTP 請(qǐng)求與響應(yīng)的過(guò)程。無(wú)論是在 PC 端還是移動(dòng)端,我們通常使用瀏覽器來(lái)上網(wǎng),上網(wǎng)流程大致來(lái)說(shuō)是這樣的:

我們打開(kāi)瀏覽器,在地址欄輸入想訪問(wèn)的網(wǎng)址,比如 http://www.cnblogs.com/。瀏覽器知道我們想要訪問(wèn)哪個(gè)網(wǎng)址后,它在后臺(tái)幫我們做了很多事情。主要就是把我們的訪問(wèn)意圖包裝成一個(gè) HTTP 請(qǐng)求,發(fā)給我們想要訪問(wèn)的網(wǎng)址所對(duì)應(yīng)的服務(wù)器。通俗點(diǎn)說(shuō)就是瀏覽器幫我們通知網(wǎng)站的服務(wù)器,說(shuō)有人來(lái)訪問(wèn)你啦,訪問(wèn)的請(qǐng)求都寫(xiě)在 HTTP 里了,你按照要求處理后告訴我,我再幫你回應(yīng)他!服務(wù)器處理了HTTP 請(qǐng)求,然后生成一段 HTTP 響應(yīng)給瀏覽器。瀏覽器解讀這個(gè)響應(yīng),把相關(guān)的內(nèi)容在瀏覽器里顯示出來(lái),于是我們就看到了網(wǎng)站的內(nèi)容。比如你訪問(wèn)了我的博客主頁(yè)http://www.cnblogs.com/derek1184405959/,服務(wù)器接收到這個(gè)請(qǐng)求后就知道用戶訪問(wèn)的是首頁(yè),首頁(yè)顯示的是全部文章列表,于是它從數(shù)據(jù)庫(kù)里把文章數(shù)據(jù)取出來(lái),生成一個(gè)寫(xiě)著這些數(shù)據(jù)的 HTML 文檔,包裝到 HTTP 響應(yīng)里發(fā)給瀏覽器,瀏覽器解讀這個(gè)響應(yīng),把 HTML 文檔顯示出來(lái),我們就看到了文章列表的內(nèi)容。

因此,Django 作為一個(gè) Web 框架,它的使命就是處理流程中的第二步。即接收瀏覽器發(fā)來(lái)的 HTTP 請(qǐng)求,返回相應(yīng)的 HTTP 響應(yīng)。于是引出這么幾個(gè)問(wèn)題:

Django 如何接收 HTTP 請(qǐng)求? Django 如何處理這個(gè) HTTP 請(qǐng)求? Django 如何生成 HTTP 響應(yīng)?

對(duì)于如何處理這些問(wèn)題,Django 有其一套規(guī)定的機(jī)制。我們按照 Django 的規(guī)定,就能開(kāi)發(fā)出所需的功能

Hello視圖函數(shù)

 我們先以一個(gè)最簡(jiǎn)單的 Hello World 為例來(lái)看看 Django 處理上述問(wèn)題的機(jī)制是怎么樣的。

 綁定url和視圖函數(shù)

 首先 Django 需要知道當(dāng)用戶訪問(wèn)不同的網(wǎng)址時(shí),應(yīng)該如何處理這些不同的網(wǎng)址(即所說(shuō)的路由)。Django 的做法是把不同的網(wǎng)址對(duì)應(yīng)的處理函數(shù)寫(xiě)在一個(gè) urls.py 文件里,當(dāng)用戶訪問(wèn)某個(gè)網(wǎng)址時(shí),Django 就去會(huì)這個(gè)文件里找,如果找到這個(gè)網(wǎng)址,就會(huì)調(diào)用和它綁定在一起的處理函數(shù)(叫做視圖函數(shù))。

 下面是具體的做法,首先在 blog 應(yīng)用的目錄下創(chuàng)建一個(gè) urls.py 文件,在 blog\urls.py 中寫(xiě)入這些代碼:

# blog/urls.py

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

urlpatterns = [
 url(r'^$',views.index,name='index'),
]
我們首先從 django.conf.urls 導(dǎo)入了 url 函數(shù),又從當(dāng)前目錄下導(dǎo)入了 views 模塊。然后我們把網(wǎng)址和處理函數(shù)的關(guān)系寫(xiě)在了 urlpatterns 列表里。

綁定關(guān)系的寫(xiě)法是把網(wǎng)址和對(duì)應(yīng)的處理函數(shù)作為參數(shù)傳給 url 函數(shù)(第一個(gè)參數(shù)是網(wǎng)址,第二個(gè)參數(shù)是處理函數(shù)),另外我們還傳遞了另外一個(gè)參數(shù) name,這個(gè)參數(shù)的值將作為處理函數(shù) index 的別名,這在以后會(huì)用到。

注意這里我們的網(wǎng)址是用正則表達(dá)式寫(xiě)的,Django 會(huì)用這個(gè)正則表達(dá)式去匹配用戶實(shí)際輸入的網(wǎng)址,如果匹配成功,就會(huì)調(diào)用其后面的視圖函數(shù)做相應(yīng)的處理。

比如說(shuō)我們本地開(kāi)發(fā)服務(wù)器的域名是 http://127.0.0.1:8000,那么當(dāng)用戶輸入網(wǎng)址 http://127.0.0.1:8000 后,Django 首先會(huì)把協(xié)議 http、域名 127.0.0.1 和端口號(hào) 8000 去掉,此時(shí)只剩下一個(gè)空字符串,而 r'^$' 的模式正是匹配一個(gè)空字符串(這個(gè)正則表達(dá)式的意思是以空字符串開(kāi)頭且以空字符串結(jié)尾),于是二者匹配,Django 便會(huì)調(diào)用其對(duì)應(yīng)的 views.index 函數(shù)。

注意:在項(xiàng)目根目錄的 blogproject\ 目錄下(即 settings.py 所在的目錄),原本就有一個(gè) urls.py 文件,這是整個(gè)工程項(xiàng)目的 URL 配置文件。而我們這里新建了一個(gè) urls.py 文件,且位于 blog 應(yīng)用下。這個(gè)文件將用于 blog 應(yīng)用相關(guān)的 URL 配置。不要把兩個(gè)文件搞混了。

編寫(xiě)視圖函數(shù)

第二步就是要實(shí)際編寫(xiě)我們的 views.index 視圖函數(shù)了,按照慣例視圖函數(shù)定義在 views.py 文件里:

from django.shortcuts import HttpResponse

def index(request):
 return HttpResponse('歡迎訪問(wèn)我的博客')
我們前面說(shuō)過(guò),Web 服務(wù)器的作用就是接收來(lái)自用戶的 HTTP 請(qǐng)求,根據(jù)請(qǐng)求內(nèi)容作出相應(yīng)的處理,并把處理結(jié)果包裝成 HTTP 響應(yīng)返回給用戶。

這個(gè)兩行的函數(shù)體現(xiàn)了這個(gè)過(guò)程。它首先接受了一個(gè)名為 request 的參數(shù),這個(gè) request就是 Django 為我們封裝好的 HTTP 請(qǐng)求,它是類 HttpRequest 的一個(gè)實(shí)例。然后我們便直接返回了一個(gè) HTTP 響應(yīng)給用戶,這個(gè) HTTP 響應(yīng)也是 Django 幫我們封裝好的,它是類 HttpResponse 的一個(gè)實(shí)例,只是我們給它傳了一個(gè)自定義的字符串參數(shù)。

瀏覽器接收到這個(gè)響應(yīng)后就會(huì)在頁(yè)面上顯示出我們傳遞的內(nèi)容:歡迎訪問(wèn)我的博客

配置項(xiàng)目URL

還差最后一步了,我們前面建立了一個(gè) urls.py 文件,并且綁定了 URL 和視圖函數(shù) index,但是 Django 并不知道。Django 匹配 URL 模式是在 blogproject\ 目錄(即 settings.py 文件所在的目錄)的 urls.py 下的,所以我們要把 blog 應(yīng)用下的 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')),
]
我們這里導(dǎo)入了一個(gè) include 函數(shù),然后利用這個(gè)函數(shù)把 blog 應(yīng)用下的 urls.py 文件包含了進(jìn)來(lái)。此外 include 前還有一個(gè) r'',這是一個(gè)空字符串。這里也可以寫(xiě)其它字符串,Django 會(huì)把這個(gè)字符串和后面 include 的 urls.py 文件中的 URL 拼接。比如說(shuō)如果我們這里把 r'' 改成 r'blog/',而我們?cè)?blog.urls 中寫(xiě)的 URL 是 r'^$',即一個(gè)空字符串。那么 Django 最終匹配的就是 blog/ 加上一個(gè)空字符串,即 blog/。

運(yùn)行結(jié)果

運(yùn)行 python manage.py runserver 打開(kāi)開(kāi)發(fā)服務(wù)器,在瀏覽器輸入開(kāi)發(fā)服務(wù)器的地址 http://127.0.0.1:8000/,可以看到 Django 返回的內(nèi)容了。

5.2.使用Django模板系統(tǒng)

這基本上就上 Django 的開(kāi)發(fā)流程了,寫(xiě)好處理 HTTP 請(qǐng)求和返回 HTTP 響應(yīng)的視圖函數(shù),然后把視圖函數(shù)綁定到相應(yīng)的 URL 上。

但是等一等!我們看到在視圖函數(shù)里返回的是一個(gè) HttpResponse 類的實(shí)例,我們給它傳入了一個(gè)希望顯示在用戶瀏覽器上的字符串。但是我們的博客不可能只顯示這么一句話,它有可能會(huì)顯示很長(zhǎng)很長(zhǎng)的內(nèi)容。比如我們發(fā)布的博客文章列表,或者一大段的博客文章。我們不能每次都把這些大段大段的內(nèi)容傳給 HttpResponse。

Django 對(duì)這個(gè)問(wèn)題給我們提供了一個(gè)很好的解決方案,叫做模板系統(tǒng)。Django 要我們把大段的文本寫(xiě)到一個(gè)文件里,然后 Django 自己會(huì)去讀取這個(gè)文件,再把讀取到的內(nèi)容傳給 HttpResponse。讓我們用模板系統(tǒng)來(lái)改造一下上面的例子。

首先在我們的項(xiàng)目根目錄(即 manage.py 文件所在目錄)下建立一個(gè)名為 templates 的文件夾,用來(lái)存放我們的模板。然后在 templates\ 目錄下建立一個(gè)名為 blog 的文件夾,用來(lái)存放和 blog 應(yīng)用相關(guān)的模板。

當(dāng)然模板存放在哪里是無(wú)關(guān)緊要的,只要 Django 能夠找到的就好。但是我們建立這樣的文件夾結(jié)構(gòu)的目的是把不同應(yīng)用用到的模板隔離開(kāi)來(lái),這樣方便以后維護(hù)。我們?cè)?templates\blog 目錄下建立一個(gè)名為 index.html 的文件

在 templates\blog\index.html 文件里寫(xiě)入下面的代碼:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>{{ title }}</title>
</head>
<body>
 <h1>{{ welcome }}</h1>
</body>
</html>
這是一個(gè)標(biāo)準(zhǔn)的 HTML 文檔,只是里面有兩個(gè)比較奇怪的地方:{{ title }},{{ welcome }}。這是 Django 規(guī)定的語(yǔ)法。用 {{ }} 包起來(lái)的變量叫做模板變量。Django 在渲染這個(gè)模板的時(shí)候會(huì)根據(jù)我們傳遞給模板的變量替換掉這些變量。最終在模板中顯示的將會(huì)是我們傳遞的值。

模板寫(xiě)好了,還得告訴 Django 去哪里找模板,在 settings.py 文件里設(shè)置一下模板文件所在的路徑。在 settings.py 找到 TEMPLATES 選項(xiàng),其中 DIRS 就是設(shè)置模板的路徑,在 [] 中寫(xiě)入 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 在配置開(kāi)頭前面定義的變量,記錄的是工程根目錄 blogproject\ 的值(注意是最外層的 blogproject\ 目錄)。在這個(gè)目錄下有模板文件所在的目錄 templates\,于是利用os.path.join 把這兩個(gè)路徑連起來(lái),構(gòu)成完整的模板路徑,Django 就知道去這個(gè)路徑下面找我們的模板了。

視圖函數(shù)可以改一下了:

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

def index(request):
 return render(request, 'blog/index.html', context={
      'title': '我的博客首頁(yè)',
      'welcome': '歡迎訪問(wèn)我的博客首頁(yè)'
     })
這里我們不再是直接把字符串傳給 HttpResponse 了,而是調(diào)用 Django 提供的 render 函數(shù)。這個(gè)函數(shù)根據(jù)我們傳入的參數(shù)來(lái)構(gòu)造 HttpResponse。

我們首先把 HTTP 請(qǐng)求傳了進(jìn)去,然后 render 根據(jù)第二個(gè)參數(shù)的值 blog/index.html 找到這個(gè)模板文件并讀取模板中的內(nèi)容。之后 render 根據(jù)我們傳入的 context 參數(shù)的值把模板中的變量替換為我們傳遞的變量的值,{{ title }} 被替換成了 context 字典中 title對(duì)應(yīng)的值,同理 {{ welcome }} 也被替換成相應(yīng)的值。

最終,我們的 HTML 模板中的內(nèi)容字符串被傳遞給 HttpResponse 對(duì)象并返回給瀏覽器(Django 在 render 函數(shù)里隱式地幫我們完成了這個(gè)過(guò)程),這樣用戶的瀏覽器上便顯示出了我們寫(xiě)的 HTML 模板的內(nèi)容

六、真正的Django博客首頁(yè)視圖

 在此之前我們已經(jīng)編寫(xiě)了 Blog 的首頁(yè)視圖,并且配置了 URL 和模板,讓 Django 能夠正確地處理 HTTP 請(qǐng)求并返回合適的 HTTP 響應(yīng)。不過(guò)我們僅僅在首頁(yè)返回了一句話:歡迎訪問(wèn)我的博客。這是個(gè) Hello World 級(jí)別的視圖函數(shù),我們需要編寫(xiě)真正的首頁(yè)視圖函數(shù),當(dāng)用戶訪問(wèn)我們的博客首頁(yè)時(shí),他將看到我們發(fā)表的博客文章列表。像前面演示的那樣

 

6.1.首頁(yè)視圖函數(shù)

上一節(jié)我們闡明了 Django 的開(kāi)發(fā)流程。即首先配置 URL,把 URL 和相應(yīng)的視圖函數(shù)綁定,一般寫(xiě)在 urls.py 文件里,然后在工程的 urls.py 文件引入。其次是編寫(xiě)視圖函數(shù),視圖中需要渲染模板,我們也在 settings.py 中進(jìn)行了模板相關(guān)的配置,讓 Django 能夠找到需要渲染的模板。最后把渲染完成的 HTTP 響應(yīng)返回就可以了。相關(guān)的配置和準(zhǔn)備工作都在之前完成了,這里我們只需專心編寫(xiě)視圖函數(shù),讓它實(shí)現(xiàn)我們想要的功能即可。

首頁(yè)的視圖函數(shù)其實(shí)很簡(jiǎn)單,代碼像這樣:

# 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})
 
我們?cè)?jīng)在前面的章節(jié)講解過(guò)模型管理器 objects 的使用。這里我們使用 all() 方法從數(shù)據(jù)庫(kù)里獲取了全部的文章,存在了 post_list 變量里。all 方法返回的是一個(gè) QuerySet(可以理解成一個(gè)類似于列表的數(shù)據(jù)結(jié)構(gòu)),由于通常來(lái)說(shuō)博客文章列表是按文章發(fā)表時(shí)間倒序排列的,即最新的文章排在最前面,所以我們緊接著調(diào)用了 order_by 方法對(duì)這個(gè)返回的 queryset 進(jìn)行排序。排序依據(jù)的字段是 created_time,即文章的創(chuàng)建時(shí)間。- 號(hào)表示逆序,如果不加 - 則是正序。 接著如之前所做,我們渲染了 blog\index.html 模板文件,并且把包含文章列表數(shù)據(jù)的 post_list 變量傳給了模板。

6.2.處理靜態(tài)文件

我們的項(xiàng)目使用了從網(wǎng)上下載的一套博客模板 點(diǎn)擊這里下載全套模板。這里面除了HTML 文檔外,還包含了一些 CSS 文件和 JavaScript 文件以讓網(wǎng)頁(yè)呈現(xiàn)出我們現(xiàn)在看到的樣式。同樣我們需要對(duì) Django 做一些必要的配置,才能讓 Django 知道如何在開(kāi)發(fā)服務(wù)器中引入這些 CSS 和 JavaScript 文件,這樣才能讓博客頁(yè)面的 CSS 樣式生效。

按照慣例,我們把 CSS 和 JavaScript 文件放在 blog 應(yīng)用的 static\ 目錄下。因此,先在 blog 應(yīng)用下建立一個(gè) static 文件夾。同時(shí),為了避免和其它應(yīng)用中的 CSS 和 JavaScript 文件命名沖突(別的應(yīng)用下也可能有和 blog 應(yīng)用下同名的 CSS 、JavaScript 文件),我們?cè)僭?static\ 目錄下建立一個(gè) blog 文件夾,把下載的博客模板中的 css 和 js 文件夾連同里面的全部文件一同拷貝進(jìn)這個(gè)目錄。目錄結(jié)構(gòu)

用下載的博客模板中的 index.html 文件替換掉之前我們自己寫(xiě)的 index.html 文件。如果你好奇,現(xiàn)在就可以運(yùn)行開(kāi)發(fā)服務(wù)器,看看首頁(yè)是什么樣子。

 

如圖所示,你會(huì)看到首頁(yè)顯示的樣式非常混亂,原因是瀏覽器無(wú)法正確加載 CSS 等樣式文件。需要以 Django 的方式來(lái)正確地處理 CSS 和 JavaScript 等靜態(tài)文件的加載路徑。CSS 樣式文件通常在 HTML 文檔的 head 標(biāo)簽里引入,打開(kāi) index.html 文件,在文件的開(kāi)始處找到 head 標(biāo)簽包裹的內(nèi)容,大概像這樣:

<head>
 <title>Black &amp; 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" rel="external nofollow" rel="external nofollow" >
 <link rel="stylesheet"  rel="external nofollow" rel="external nofollow" rel="external nofollow" >
 <link rel="stylesheet" href="css/pace.css" rel="external nofollow" >
 <link rel="stylesheet" href="css/custom.css" rel="external nofollow" >

 <!-- 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>
</head>
 

CSS 樣式文件的路徑在 link 標(biāo)簽的 href 屬性里,而 JavaScript 文件的路徑在 script 標(biāo)簽的 src 屬性里。可以看到諸如 `href="css/bootstrap.min.css" rel="external nofollow" rel="external nofollow" 或者 src="js/jquery-2.1.3.min.js" 這樣的引用,由于引用文件的路徑不對(duì),所以瀏覽器引入這些文件失敗。我們需要把它們改成正確的路徑。把代碼改成下面樣子,正確地引入 static 文件下的 CSS 和 JavaScript 文件:

{% load staticfiles %}

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

 <!-- meta -->
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1">

 <!-- css -->
 <link rel="stylesheet" href="{% static 'blog/css/bootstrap.min.css' %}" rel="external nofollow" rel="external nofollow" >
 <link rel="stylesheet"  rel="external nofollow" rel="external nofollow" rel="external nofollow" >
 <link rel="stylesheet" href="{% static 'blog/css/pace.css' %}" rel="external nofollow" rel="external nofollow" >
 <link rel="stylesheet" href="{% static 'blog/css/custom.css' %}" rel="external nofollow" >

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

<!-- 其它內(nèi)容 -->

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

</body>
</html>
我們把引用路徑放在了一個(gè)奇怪的符號(hào)里,例如:href="{% static 'blog/css/bootstrap.min.css' %}" rel="external nofollow" rel="external nofollow" 。用 {% %} 包裹起來(lái)的叫做模板標(biāo)簽。我們前面說(shuō)過(guò)用 {{ }} 包裹起來(lái)的叫做模板變量,其作用是在最終渲染的模板里顯示由視圖函數(shù)傳過(guò)來(lái)的變量值。而這里我們使用的模板標(biāo)簽的功能則類似于函數(shù),例如這里的 static 模板標(biāo)簽,它把跟在后面的字符串 'css/bootstrap.min.css' 轉(zhuǎn)換成正確的文件引入路徑。這樣 css 和 js 文件才能被正確加載,樣式才能正常顯示。

為了能在模板中使用 {% static %} 模板標(biāo)簽,別忘了在最頂部添加 {% load staticfiles %} 。static 模板標(biāo)簽位于 staticfiles 模塊中,只有通過(guò) load 模板標(biāo)簽將該模塊引入后,才能在模板中使用 {% static %} 標(biāo)簽。

替換完成后你可以刷新頁(yè)面并看看網(wǎng)頁(yè)的源代碼,看一看 {% static %} 模板標(biāo)簽在頁(yè)面渲染后究竟被替換成了什么樣的值。例如我們可以看到

<link rel="stylesheet" href="{% static 'blog/css/pace.css' %}" rel="external nofollow" rel="external nofollow" >

這一部分最終在瀏覽器中顯示的是:

<link rel="stylesheet" href="/static/blog/css/pace.css" rel="external nofollow" >

這正是 pace.css 文件所在的路徑,其它的文件路徑也被類似替換。可以看到 {% static %} 標(biāo)簽的作用實(shí)際就是把后面的字符串加了一個(gè) /static/ 前綴,比如 {% static 'blog/css/pace.css' %} 最終渲染的值是 /static/blog/css/pace.css。而 /static/ 前綴是我們?cè)?settings.py 文件中通過(guò) STATIC_URL = '/static/' 指定的。事實(shí)上,如果我們直接把引用路徑寫(xiě)成 /static/blog/css/pace.css 也是可以的,那么為什么要使用 {% static %} 標(biāo)簽?zāi)??想一下,目?URL 的前綴是 /static/,如果哪一天因?yàn)槟承┰颍覀冃枰?/static/ 改成 /resource/,如果你是直接寫(xiě)的引用路勁而沒(méi)有使用 static 模板標(biāo)簽,那么你可能需要改 N 個(gè)地方。如果你使用了 static 模板標(biāo)簽,那么只要在 settings.py 處改一個(gè)地方就可以了,即把 STATIC_URL = '/static/' 改成 STATIC_URL = '/resource/'。

注意這里有一個(gè) CSS 文件的引入

<link rel="stylesheet" href="

我們沒(méi)有使用模板標(biāo)簽,因?yàn)檫@里的引用的文件是一個(gè)外部文件,不是我們項(xiàng)目里 static\blog\css\ 目錄下的文件,因此無(wú)需使用模板標(biāo)簽。

正確引入了靜態(tài)文件后樣式顯示正常了。

6.3修改模板

 目前我們看到的只是模板中預(yù)先填充的一些數(shù)據(jù),我們得讓它顯示從數(shù)據(jù)庫(kù)中獲取的文章數(shù)據(jù)。下面來(lái)稍微改造一下模板:

在模板 index.html 中你會(huì)找到一系列 article 標(biāo)簽:

templates/blog/index.html

...
<article class="post post-1">
 ...
</article>

<article class="post post-2">
 ...
</article>

<article class="post post-3">
 ...
</article>
...

這里面包裹的內(nèi)容顯示的就是文章數(shù)據(jù)了。我們前面在視圖函數(shù) index 里給模板傳了一個(gè) post_list 變量,它里面包含著從數(shù)據(jù)庫(kù)中取出的文章列表數(shù)據(jù)。就像 Python 一樣,我們可以在模板中循環(huán)這個(gè)列表,把文章一篇篇循環(huán)出來(lái),然后一篇篇顯示文章的數(shù)據(jù)。要在模板中使用循環(huán),需要使用到前面提到的模板標(biāo)簽,這次使用 {% for %} 模板標(biāo)簽。將 index.html 中多余的 article 標(biāo)簽刪掉,只留下一個(gè) article 標(biāo)簽,然后寫(xiě)上下列代碼:

templates/blog/index.html

...
{% for post in post_list %}
 <article class="post post-{{ post.pk }}">
 ...
 </article>
{% empty %}
 <div class="no-post">暫時(shí)還沒(méi)有發(fā)布的文章!</div>
{% endfor %}
...

可以看到語(yǔ)法和 Python 的 for 循環(huán)類似,只是被 {% %} 這樣一個(gè)模板標(biāo)簽符號(hào)包裹著。{% empty %} 的作用是當(dāng) post_list 為空,即數(shù)據(jù)庫(kù)里沒(méi)有文章時(shí)顯示 {% empty %} 下面的內(nèi)容,最后我們用 {% endfor %} 告訴 Django 循環(huán)在這里結(jié)束了。

你可能不太理解模板中的 post 和 post_list 是什么。post_list 是一個(gè) QuerySet(類似于一個(gè)列表的數(shù)據(jù)結(jié)構(gòu)),其中每一項(xiàng)都是之前定義在 blog\models.py 中的 Post 類的實(shí)例,且每個(gè)實(shí)例分別對(duì)應(yīng)著數(shù)據(jù)庫(kù)中每篇文章的記錄。因此我們循環(huán)遍歷 post_list ,每一次遍歷的結(jié)果都保存在 post變量里。所以我們使用模板變量來(lái)顯示 post 的屬性值。例如這里的 {{ post.pk }}(pk 是 primary key 的縮寫(xiě),即 post 對(duì)應(yīng)于數(shù)據(jù)庫(kù)中記錄的 id 值,該屬性盡管我們沒(méi)有顯示定義,但是 Django 會(huì)自動(dòng)為我們添加)。

 我們把標(biāo)題替換成 post 的 title 屬性值。注意要把它包裹在模板變量里,因?yàn)樗罱K要被替換成實(shí)際的 title 值。

<h1 class="entry-title">
 <a href="single.html" rel="external nofollow" >{{ post.title }}</a>
</h1>

下面這 5 個(gè) span 標(biāo)簽里分別顯示了分類(category)、文章發(fā)布時(shí)間、文章作者、評(píng)論數(shù)、閱讀量。

<div class="entry-meta">
 <span class="post-category"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >Django 博客教程</a></span>
 <span class="post-date"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ><time class="entry-date"
           datetime="2012-11-09T23:15:57+00:00">2017年5月11日</time></a></span>
 <span class="post-author"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >追夢(mèng)人物</a></span>
 <span class="comments-link"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >4 評(píng)論</a></span>
 <span class="views-count"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >588 閱讀</a></span>
</div>

再次替換掉一些數(shù)據(jù),由于評(píng)論數(shù)和閱讀量暫時(shí)沒(méi)法替換,因此先留著,我們?cè)谥髮?shí)現(xiàn)了這些功能后再來(lái)修改它,目前只替換分類、文章發(fā)布時(shí)間、文章作者:

<div class="entry-meta">
 <span class="post-category"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{{ post.category.name }}</a></span>
 <span class="post-date"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ><time class="entry-date"
           datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span>
 <span class="post-author"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{{ post.author }}</a></span>
 <span class="comments-link"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >4 評(píng)論</a></span>
 <span class="views-count"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >588 閱讀</a></span>
</div>

這里 p 標(biāo)簽里顯示的是摘要

<div class="entry-content clearfix">
 <p>免費(fèi)、中文、零基礎(chǔ),完整的項(xiàng)目,基于最新版 Django 1.10 和 Python 3.5。帶你從零開(kāi)始一步步開(kāi)發(fā)屬于自己的博客網(wǎng)站,幫助你以最快的速度掌握 Django
 開(kāi)發(fā)的技巧...</p>
 <div class="read-more cl-effect-14">
 <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="more-link">繼續(xù)閱讀 <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="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="more-link">繼續(xù)閱讀 <span class="meta-nav">→</span></a>
 </div>
</div>

再次訪問(wèn)首頁(yè),它顯示:暫時(shí)還沒(méi)有發(fā)布的文章!好吧,做了這么多工作,但是數(shù)據(jù)庫(kù)中其實(shí)還沒(méi)有任何數(shù)據(jù)呀!接下來(lái)我們就實(shí)際寫(xiě)幾篇文章保存到數(shù)據(jù)庫(kù)里,看看顯示的效果究竟如何。

 七、在Django Admin后臺(tái)發(fā)布文章

 在此之前我們完成了 Django 博客首頁(yè)視圖的編寫(xiě),我們希望首頁(yè)展示發(fā)布的博客文章列表,但是它卻抱怨:暫時(shí)還沒(méi)有發(fā)布的文章!如它所言,我們確實(shí)還沒(méi)有發(fā)布任何文章,本節(jié)我們將使用 Django 自帶的 Admin 后臺(tái)來(lái)發(fā)布我們的博客文章。

7.1.在Django后臺(tái)注冊(cè)模型

前面已經(jīng)用python manage.py createsuperuser,創(chuàng)建了超級(jí)用戶。

要在后臺(tái)注冊(cè)我們自己創(chuàng)建的幾個(gè)模型,這樣 Django Admin 才能知道它們的存在,注冊(cè)非常簡(jiǎn)單,只需要在 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)

運(yùn)行開(kāi)發(fā)服務(wù)器,訪問(wèn) http://127.0.0.1:8000/admin/ ,就進(jìn)入了到了Django Admin 后臺(tái)登錄頁(yè)面,輸入剛才創(chuàng)建的管理員賬戶密碼就可以登錄到后臺(tái)了。

可以看到我們剛才注冊(cè)的三個(gè)模型了,點(diǎn)擊 Posts 后面的增加按鈕,將進(jìn)入添加 Post 的頁(yè)面,也就是新增博客文章。然后在相關(guān)的地方輸入一些測(cè)試用的內(nèi)容,增加完后點(diǎn)擊保存,這樣文章就添加完畢了,你也可以多添加幾篇看看效果。注意每篇文章必須有一個(gè)分類,在添加文章時(shí)你可以選擇已有分類。如果數(shù)據(jù)庫(kù)中還沒(méi)有分類,在選擇分類時(shí)點(diǎn)擊 Category 后面的 + 按鈕新增一個(gè)分類即可。

 訪問(wèn) http://127.0.0.1:8000/ 首頁(yè),你就可以看到你添加的文章列表了,下面是我所在環(huán)境的效果圖:

7.2.定制Admin后臺(tái)

 在 admin post 列表頁(yè)面,我們只看到了文章的標(biāo)題,但是我們希望它顯示更加詳細(xì)的信息,這需要我們來(lái)定制 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)

這里只是為了簡(jiǎn)單地到達(dá)期望效果

刷新 Admin Post 列表頁(yè)面,可以看到顯示的效果好多了。

八、博客文章詳情頁(yè)

 首頁(yè)展示的是所有文章的列表,當(dāng)用戶看到感興趣的文章時(shí),他點(diǎn)擊文章的標(biāo)題或者繼續(xù)閱讀的按鈕,應(yīng)該跳轉(zhuǎn)到文章的詳情頁(yè)面來(lái)閱讀文章的詳細(xì)內(nèi)容?,F(xiàn)在讓我們來(lái)開(kāi)發(fā)博客的詳情頁(yè)面,有了前面的基礎(chǔ),開(kāi)發(fā)流程都是一樣的了:首先配置 URL,即把相關(guān)的 URL 和視圖函數(shù)綁定在一起,然后實(shí)現(xiàn)視圖函數(shù),編寫(xiě)模板并讓視圖函數(shù)渲染模板。

8.1.設(shè)置文章詳情頁(yè)的url

回顧一下我們首頁(yè)視圖的 URL,在 blog\urls.py 文件里,我們寫(xiě)了:

blog/urls.py

from django.conf.urls import url

from . import views

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

首頁(yè)視圖匹配的 URL 去掉域名后其實(shí)就是一個(gè)空的字符串。對(duì)文章詳情視圖而言,每篇文章對(duì)應(yīng)著不同的 URL。比如我們可以把文章詳情頁(yè)面對(duì)應(yīng)的視圖設(shè)計(jì)成這個(gè)樣子:當(dāng)用戶訪問(wèn) <網(wǎng)站域名>/post/1/ 時(shí),顯示的是第一篇文章的內(nèi)容,而當(dāng)用戶訪問(wèn) <網(wǎng)站域名>/post/2/ 時(shí),顯示的是第二篇文章的內(nèi)容,這里數(shù)字代表了第幾篇文章,也就是數(shù)據(jù)庫(kù)中 Post 記錄的 id 值。下面依照這個(gè)規(guī)則來(lái)綁定 URL 和視圖:

# blog/urls.py

from django.conf.urls import url
from . import views
app_name = 'blog'

urlpatterns = [
 url(r'^$',views.index,name='index'),
 url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail'),
]

Django 使用正則表達(dá)式來(lái)匹配用戶訪問(wèn)的網(wǎng)址。這里 r'^post/(?P<pk>[0-9]+)/$' 整個(gè)正則表達(dá)式剛好匹配我們上面定義的 URL 規(guī)則。這條正則表達(dá)式的含義是,以 post/ 開(kāi)頭,后跟一個(gè)至少一位數(shù)的數(shù)字,并且以 / 符號(hào)結(jié)尾,如 post/1/、 post/255/ 等都是符合規(guī)則的,[0-9]+ 表示一位或者多位數(shù)。此外這里 (?P<pk>[0-9]+) 表示命名捕獲組,其作用是從用戶訪問(wèn)的 URL 里把括號(hào)內(nèi)匹配的字符串捕獲并作為關(guān)鍵字參數(shù)傳給其對(duì)應(yīng)的視圖函數(shù) detail。比如當(dāng)用戶訪問(wèn) post/255/ 時(shí)(注意 Django 并不關(guān)心域名,而只關(guān)心去掉域名后的相對(duì) URL),被括起來(lái)的部分 (?P<pk>[0-9]+) 匹配 255,那么這個(gè) 255 會(huì)在調(diào)用視圖函數(shù) detail 時(shí)被傳遞進(jìn)去,實(shí)際上視圖函數(shù)的調(diào)用就是這個(gè)樣子:detail(request, pk=255)。我們這里必須從 URL 里捕獲文章的 id,因?yàn)橹挥羞@樣我們才能知道用戶訪問(wèn)的究竟是哪篇文章。

此外我們通過(guò) app_name='blog' 告訴 Django 這個(gè) urls.py 模塊是屬于 blog 應(yīng)用的,這種技術(shù)叫做視圖函數(shù)命名空間。我們看到 blog\urls.py 目前有兩個(gè)視圖函數(shù),并且通過(guò) name 屬性給這些視圖函數(shù)取了個(gè)別名,分別是 index、detail。但是一個(gè)復(fù)雜的 Django 項(xiàng)目可能不止這些視圖函數(shù),例如一些第三方應(yīng)用中也可能有叫 index、detail 的視圖函數(shù),那么怎么把它們區(qū)分開(kāi)來(lái),防止沖突呢?方法就是通過(guò) app_name 來(lái)指定命名空間,命名空間具體如何使用將在下面介紹。

為了方便地生成上述的 URL,我們?cè)?nbsp;Post 類里定義一個(gè) get_absolute_url方法,注意 Post 本身是一個(gè) 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 __str__(self):
  return self.title

 # 自定義 get_absolute_url 方法
 # 記得從 django.urls 中導(dǎo)入 reverse 函數(shù)
 def get_absolute_url(self):
  return reverse('blog:detail', kwargs={'pk': self.pk})

注意到 URL 配置中的 url(r'^post/(?P<pk>[0-9]+)/$', views.detail, name='detail') ,我們?cè)O(shè)定的 name='detail' 在這里派上了用場(chǎng)??吹竭@個(gè) reverse 函數(shù),它的第一個(gè)參數(shù)的值是 'blog:detail',意思是 blog 應(yīng)用下的 name=detail 的函數(shù),由于我們?cè)谏厦嫱ㄟ^(guò) app_name = 'blog' 告訴了 Django 這個(gè) URL 模塊是屬于 blog 應(yīng)用的,因此 Django 能夠順利地找到 blog 應(yīng)用下 name 為 detail 的視圖函數(shù),于是 reverse 函數(shù)會(huì)去解析這個(gè)視圖函數(shù)對(duì)應(yīng)的 URL,我們這里 detail 對(duì)應(yīng)的規(guī)則就是 post/(?P<pk>[0-9]+)/這個(gè)正則表達(dá)式,而正則表達(dá)式部分會(huì)被后面?zhèn)魅氲膮?shù) pk 替換,所以,如果 Post 的 id(或者 pk,這里 pk 和 id 是等價(jià)的) 是 255 的話,那么 get_absolute_url 函數(shù)返回的就是 /post/255/ ,這樣 Post 自己就生成了自己的 URL。

8.2.編寫(xiě)detail視圖函數(shù)

 接下來(lái)就是實(shí)現(xiàn)我們的 detail 視圖函數(shù)了:

# blog/views.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})

視圖函數(shù)很簡(jiǎn)單,它根據(jù)我們從 URL 捕獲的文章 id(也就是 pk,這里 pk 和 id 是等價(jià)的)獲取數(shù)據(jù)庫(kù)中文章 id 為該值的記錄,然后傳遞給模板。注意這里我們用到了從 django.shortcuts 模塊導(dǎo)入的 get_object_or_404 方法,其作用就是當(dāng)傳入的 pk 對(duì)應(yīng)的 Post 在數(shù)據(jù)庫(kù)存在時(shí),就返回對(duì)應(yīng)的 post,如果不存在,就給用戶返回一個(gè) 404 錯(cuò)誤,表明用戶請(qǐng)求的文章不存在。

8.3.編寫(xiě)詳情頁(yè)模板

接下來(lái)就是書(shū)寫(xiě)模板文件,從下載的博客模板中把 single.html 拷貝到 templates\blog 目錄下(和 index.html 在同一級(jí)目錄),然后改名為 detail.html。

在 index 頁(yè)面博客文章列表的標(biāo)題和繼續(xù)閱讀按鈕寫(xiě)上超鏈接跳轉(zhuǎn)的鏈接,即文章 post 對(duì)應(yīng)的詳情頁(yè)的 URL,讓用戶點(diǎn)擊后可以跳轉(zhuǎn)到 detail 頁(yè)面:

 <article class="post post-{{ post.pk }}">
     <header class="entry-header">
      <h1 class="entry-title">
       <a href="{{ post.get_absolute_url }}" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{{ post.title }}</a>
      </h1>
      <div class="entry-meta">
       <span class="post-category"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{{ post.category.name }}</a></span>
       <span class="post-date"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ><time class="entry-date"
                  datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span>
       <span class="post-author"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{{ post.author }}</a></span>
       <span class="comments-link"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >4 評(píng)論</a></span>
       <span class="views-count"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >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 }}" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="more-link">繼續(xù)閱讀 <span class="meta-nav">→</span></a>
      </div>
     </div>
    </article>

這里我們修改兩個(gè)地方,第一個(gè)是文章標(biāo)題處:

<h1 class="entry-title">
 <a href="{{ post.get_absolute_url }}" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{{ post.title }}</a>
</h1>

我們把 a 標(biāo)簽的 href 屬性的值改成了 {{ post.get_absolute_url }}?;仡櫼幌履0遄兞康挠梅?,由于 get_absolute_url 這個(gè)方法(我們定義在 Post 類中的)返回的是 post 對(duì)應(yīng)的 URL,因此這里 {{ post.get_absolute_url }} 最終會(huì)被替換成該 post 自身的 URL。

同樣,第二處修改的是繼續(xù)閱讀按鈕的鏈接:

<a href="{{ post.get_absolute_url }}" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="more-link">繼續(xù)閱讀 <span class="meta-nav">→</span>
</a>

這樣當(dāng)我們點(diǎn)擊首頁(yè)文章的標(biāo)題或者繼續(xù)閱讀按鈕后就會(huì)跳轉(zhuǎn)到該篇文章對(duì)應(yīng)的詳情頁(yè)面了。然而如果你嘗試跳轉(zhuǎn)到詳情頁(yè)后,你會(huì)發(fā)現(xiàn)樣式是亂的。這在 真正的 Django 博客首頁(yè) 時(shí)講過(guò),由于我們是直接復(fù)制的模板,還沒(méi)有正確地處理靜態(tài)文件。我們可以按照介紹過(guò)的方法修改靜態(tài)文件的引入路徑,但很快你會(huì)發(fā)現(xiàn)在任何頁(yè)面都是需要引入這些靜態(tài)文件,如果每個(gè)頁(yè)面都要修改會(huì)很麻煩,而且代碼都是重復(fù)的。下面就介紹 Django 模板繼承的方法來(lái)幫我們消除這些重復(fù)操作。

8.4.模板繼承

我們看到 index.html 文件和 detail.html 文件除了 main 標(biāo)簽包裹的部分不同外,其它地方都是相同的,我們可以把相同的部分抽取出來(lái),放到 base.html 里。首先在 templates\ 目錄下新建一個(gè) base.html 文件

把 index.html 的內(nèi)容全部拷貝到 base.html 文件里,然后刪掉 main 標(biāo)簽包裹的內(nèi)容,替換成如下的內(nèi)容。

templates/base.html

...
<main class="col-md-8">
 {% block main %}
 {% endblock main %}
</main>
<aside class="col-md-4">
 {% block toc %}
 {% endblock toc %}
 ...
</aside>
...

這里 block 也是一個(gè)模板標(biāo)簽,其作用是占位。比如這里的 {% block main %}{% endblock main %} 是一個(gè)占位框,main 是我們給這個(gè) block 取的名字。下面我們會(huì)看到 block 標(biāo)簽的作用。同時(shí)我們也在 aside 標(biāo)簽下加了一個(gè) {% block toc %}{% endblock toc %} 占位框,因?yàn)?detail.html 中 aside 標(biāo)簽下會(huì)多一個(gè)目錄欄。當(dāng) {% block toc %}{% endblock toc %} 中沒(méi)有任何內(nèi)容時(shí),{% block toc %}{% endblock toc %} 在模板中不會(huì)顯示。但當(dāng)其中有內(nèi)容是,模板就會(huì)顯示 block 中的內(nèi)容。

在 index.html 里,我們?cè)谖募铐敳渴褂?nbsp;{% extends 'base.html' %} 繼承 base.html,這樣就把 base.html 里的代碼繼承了過(guò)來(lái),另外在 {% block main %}{% endblock main %} 包裹的地方填上 index 頁(yè)面應(yīng)該顯示的內(nèi)容:

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">暫時(shí)沒(méi)有發(fā)布文章!</div>
 {% endfor %}
 <!-- 簡(jiǎn)單分頁(yè)效果
 <div class="pagination-simple">
  <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >上一頁(yè)</a>
  <span class="current">第 6 頁(yè) / 共 11 頁(yè)</span>
  <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >下一頁(yè)</a>
 </div>
 -->
 <div class="pagination">
  ...
 </div>
{% endblock main %}

這樣 base.html 里的代碼加上 {% block main %}{% endblock main %} 里的代碼就和最開(kāi)始 index.html 里的代碼一樣了。這就是模板繼承的作用,公共部分的代碼放在 base.html 里,而其它頁(yè)面不同的部分通過(guò)替換 {% block main %}{% endblock main %} 占位標(biāo)簽里的內(nèi)容即可。

如果你對(duì)這種模板繼承還是有點(diǎn)糊涂,可以把這種繼承和 Python 中類的繼承類比。base.html 就是父類,index.html 就是子類。index.html 繼承了 base.html 中的全部?jī)?nèi)容,同時(shí)它自身還有一些內(nèi)容,這些內(nèi)容就通過(guò) “覆寫(xiě)” {% block main %}{% endblock main %}(把 block 看做是父類的屬性)的內(nèi)容添加即可。

detail 頁(yè)面處理起來(lái)就簡(jiǎn)單了,同樣繼承 base.html ,在 {% block main %}{% endblock main %} 里填充 detail.html 頁(yè)面應(yīng)該顯示的內(nèi)容,以及在 {% block toc %}{% endblock toc %} 中填寫(xiě) base.html 中沒(méi)有的目錄部分的內(nèi)容。不過(guò)目前的目錄只是占位數(shù)據(jù),我們?cè)谝院髸?huì)實(shí)現(xiàn)如何從文章中自動(dòng)摘取目錄。

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="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >教程特點(diǎn)</a>
   </li>
   <li>
    <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >誰(shuí)適合這個(gè)教程</a>
   </li>
   <li>
    <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >在線預(yù)覽</a>
   </li>
   <li>
    <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >資源列表</a>
   </li>
   <li>
    <a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >獲取幫助</a>
   </li>
  </ul>
 </div>
{% endblock toc %}

修改 article 標(biāo)簽下的一些內(nèi)容,讓其顯示文章的實(shí)際數(shù)據(jù):

<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="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{{ post.category.name }}</a></span>
  <span class="post-date"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" ><time class="entry-date"
            datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span>
  <span class="post-author"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{{ post.author }}</a></span>
  <span class="comments-link"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >4 評(píng)論</a></span>
  <span class="views-count"><a href="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >588 閱讀</a></span>
 </div>
 </header>
 <div class="entry-content clearfix">
 {{ post.body }}
 </div>
</article>

再次從首頁(yè)點(diǎn)擊一篇文章的標(biāo)題或者繼續(xù)閱讀按鈕跳轉(zhuǎn)到詳情頁(yè)面,可以看到預(yù)期效果了!

 九、支持Markdown語(yǔ)法和代碼高亮

 為了讓博客文章具有良好的排版,顯示更加豐富的格式,我們使用 Markdown 語(yǔ)法來(lái)書(shū)寫(xiě)我們的博文。Markdown 是一種 HTML 文本標(biāo)記語(yǔ)言,只要遵循它約定的語(yǔ)法格式,Markdown 的渲染器就能夠把我們寫(xiě)的文章轉(zhuǎn)換為標(biāo)準(zhǔn)的 HTML 文檔,從而讓我們的文章呈現(xiàn)更加豐富的格式,例如標(biāo)題、列表、代碼塊等等 HTML 元素。由于 Markdown 語(yǔ)法簡(jiǎn)單直觀,不用超過(guò) 5 分鐘就可以掌握常用的標(biāo)記語(yǔ)法,因此大家青睞使用 Markdown 書(shū)寫(xiě) HTML 文檔。下面讓我們的博客也支持使用 Markdown 書(shū)寫(xiě)。

相關(guān)文章

最新評(píng)論