1. 程式人生 > >Django搭建blog網站(一)

Django搭建blog網站(一)

一、前言

自學地址-->>>>>追夢人物部落格:https://www.zmrenwu.com/post/2/

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 = models.CharField(max_length=100)

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

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

    # 文章正文,我們使用了 TextField。
    # 儲存比較短的字串可以使用 CharField,但對於文章的正文來說可能會是一大段文字,因此使用 TextField 來儲存大段文字。
    body = models.TextField()

    # 這兩個列分別表示文章的建立時間和最後一次修改時間,儲存時間的欄位用 DateTimeField 型別。
    created_time = models.DateTimeField()
    modified_time = models.DateTimeField()

    # 文章摘要,可以沒有文章摘要,但預設情況下 CharField 要求我們必須存入資料,否則就會報錯。
    # 指定 CharField 的 blank=True 引數值後就可以允許空值了。
    excerpt = models.CharField(max_length=200,blank=True)

    # 我們在這裡把文章對應的資料庫表和分類、標籤對應的資料庫表關聯了起來,但是關聯形式稍微有點不同。
    # 我們規定一篇文章只能對應一個分類,但是一個分類下可以有多篇文章,所以我們使用的是 ForeignKey,即一對多的關聯關係。
    # 而對於標籤來說,一篇文章可以有多個標籤,同一個標籤下也可能有多篇文章,所以我們使用 ManyToManyField,表明這是多對多的關聯關係。
    # 同時我們規定文章可以沒有標籤,因此為標籤 tags 指定了 blank=True。
    category = models.ForeignKey(Category,on_delete=models.CASCADE)
    tags = models.ManyToManyField(Tag,blank=True)

    # 文章作者,這裡 User 是從 django.contrib.auth.models 匯入的。
    # django.contrib.auth 是 Django 內建的應用,專門用於處理網站使用者的註冊、登入等流程,User 是 Django 為我們已經寫好的使用者模型。
    # 這裡我們通過 ForeignKey 把文章和 User 關聯了起來。
    # 因為我們規定一篇文章只能有一個作者,而一個作者可能會寫多篇文章,因此這是一對多的關聯關係,和 Category 類似。
    author = models.ForeignKey(User,on_delete=models.CASCADE)
複製程式碼

四、遷移資料庫

4.1.設定資料庫為Mysql

更改setting.py預設配置

複製程式碼
# DATABASES = {
#     '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__ 方法:

__str__

定義好 __str__ 方法後,直譯器顯示的內容將會是 __str__ 方法返回的內容。這裡 Category 返回分類名 name ,Tag 返回標籤名,而 Post 返回它的 title

 exit() 退出 Shell,再重新執行 python manage.py shell 進入 Shell。

 

可以看到返回的是我們之前存入的資料。

此外我們在建立文章時提到了通過 get 方法來獲取資料,這裡 all 方法和 get 方法的區別是:all 方法返回全部資料,是一個類似於列表的資料結構(QuerySet);而 get 返回一條記錄資料,如有多條記錄或者沒有記錄,get 方法均會丟擲相應異常。

 五、部落格首頁檢視

5.1.Django處理HTTP請求

Web 應用的互動過程其實就是 HTTP 請求與響應的過程。無論是在 PC 端還是移動端,我們通常使用瀏覽器來上網,上網流程大致來說是這樣的:

  1. 我們開啟瀏覽器,在位址列輸入想訪問的網址,比如 http://www.cnblogs.com/。
  2. 瀏覽器知道我們想要訪問哪個網址後,它在後臺幫我們做了很多事情。主要就是把我們的訪問意圖包裝成一個 HTTP 請求,發給我們想要訪問的網址所對應的伺服器。通俗點說就是瀏覽器幫我們通知網站的伺服器,說有人來訪問你啦,訪問的請求都寫在 HTTP 裡了,你按照要求處理後告訴我,我再幫你迴應他!
  3. 伺服器處理了HTTP 請求,然後生成一段 HTTP 響應給瀏覽器。瀏覽器解讀這個響應,把相關的內容在瀏覽器裡顯示出來,於是我們就看到了網站的內容。比如你訪問了我的部落格主頁http://www.cnblogs.com/derek1184405959/,伺服器接收到這個請求後就知道使用者訪問的是首頁,首頁顯示的是全部文章列表,於是它從資料庫裡把文章資料取出來,生成一個寫著這些資料的 HTML 文件,包裝到 HTTP 響應裡發給瀏覽器,瀏覽器解讀這個響應,把 HTML 文件顯示出來,我們就看到了文章列表的內容。

因此,Django 作為一個 Web 框架,它的使命就是處理流程中的第二步。即接收瀏覽器發來的 HTTP 請求,返回相應的 HTTP 響應。於是引出這麼幾個問題:

  1. Django 如何接收 HTTP 請求?
  2. Django 如何處理這個 HTTP 請求?
  3. Django 如何生成 HTTP 響應?

對於如何處理這些問題,Django 有其一套規定的機制。我們按照 Django 的規定,就能開發出所需的功能

Hello檢視函式

 我們先以一個最簡單的 Hello World 為例來看看 Django 處理上述問題的機制是怎麼樣的。

 繫結url和檢視函式

 首先 Django 需要知道當用戶訪問不同的網址時,應該如何處理這些不同的網址(即所說的路由)。Django 的做法是把不同的網址對應的處理函式寫在一個 urls.py 檔案裡,當用戶訪問某個網址時,Django 就去會這個檔案裡找,如果找到這個網址,就會呼叫和它繫結在一起的處理函式(叫做檢視函式)。

 下面是具體的做法,首先在 blog 應用的目錄下建立一個 urls.py 檔案,在 blog\urls.py 中寫入這些程式碼:

複製程式碼
# blog/urls.py

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

urlpatterns = [
    url(r'^$',views.index,name='index'),
]
複製程式碼

我們首先從 django.conf.urls 匯入了 url 函式,又從當前目錄下匯入了 views 模組。然後我們把網址和處理函式的關係寫在了 urlpatterns 列表裡。

繫結關係的寫法是把網址和對應的處理函式作為引數傳給 url 函式(第一個引數是網址,第二個引數是處理函式),另外我們還傳遞了另外一個引數 name,這個引數的值將作為處理函式 index 的別名,這在以後會用到。

注意這裡我們的網址是用正則表示式寫的,Django 會用這個正則表示式去匹配使用者實際輸入的網址,如果匹配成功,就會呼叫其後面的檢視函式做相應的處理。

比如說我們本地開發伺服器的域名是 http://127.0.0.1:8000,那麼當用戶輸入網址 http://127.0.0.1:8000 後,Django 首先會把協議 http、域名 127.0.0.1 和埠號 8000 去掉,此時只剩下一個空字串,而 r'^$' 的模式正是匹配一個空字串(這個正則表示式的意思是以空字串開頭且以空字串結尾),於是二者匹配,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>

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

    <!-- css -->
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
    <link rel="stylesheet" href="css/pace.css">
    <link rel="stylesheet" href="css/custom.css">

    <!-- 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 標籤的 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>

    <!-- 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' %}">
    <link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
    <link rel="stylesheet" href="{% static 'blog/css/pace.css' %}">
    <link rel="stylesheet" href="{% static 'blog/css/custom.css' %}">

    <!-- js -->
    <script src="{% static 'blog/js/jquery-2.1.3.min.js' %}"></script>
    <script src="{% static 'blog/js/bootstrap.min.js' %}"></script>
    <script src="{% static 'blog/js/pace.min.js' %}"></script>
    <script src="{% static 'blog/js/modernizr.custom.js' %}"></script>
</head>

<body>

<!-- 其它內容 -->

<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 變數,它裡面包含著