91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python+Django搭建自己的blog網站

發布時間:2020-09-22 13:21:27 來源:腳本之家 閱讀:150 作者:zhang_derek 欄目:開發技術

一、前言

1.1.環境

python版本:3.6

Django版本:1.11.6

1.2.預覽效果

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

Python+Django搭建自己的blog網站

二、建立博客應用

2.1.建立項目和應用

創建工程blogproject

python manage.py 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.目錄結構

Python+Django搭建自己的blog網站

三、創建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
首先我們來創建一個分類和一個標簽:

Python+Django搭建自己的blog網站

我們首先導入 3 個之前寫好的模型類,然后實例化了一個 Category 類和一個 Tag 類,為他們的屬性 name 賦了值。為了讓 Django 把這些數據保存進數據庫,調用實例的 save方法即可。

創建文章之前,我們需要先創建一個 User,用于指定文章的作者。創建 User 的命令 Django 已經幫我們寫好了,依然是通過 manage.py 來運行。首先exit()退出命令交互欄,運行 python manage.py createsuperuser 命令并根據提示創建用戶:用戶名,郵箱,密碼

再次運行 python manage.py shell 進入 Python 命令交互欄,開始創建文章:

Python+Django搭建自己的blog網站

由于我們重啟了 shell,因此需要重新導入了 CategoryTagPost 以及 User。我們還導入了一個 Django 提供的輔助模塊 timezone,這是因為我們需要調用它的 now() 方法為 created_time 和 modified_time 指定時間,容易理解 now 方法返回當前時間。然后我們根據用戶名和分類名,通過 get 方法取出了存在數據庫中的 User 和 Category(取數據的方法將在下面介紹)。接著我們為文章指定了 titlebody 、created_timemodified_time值,并把它和前面創建的 Category 以及 User 關聯了起來。允許為空 excerpttags 我們就沒有為它們指定值了。

4.4.取數據

 數據已經存入數據庫了,現在要把它們取出來看看:

 Python+Django搭建自己的blog網站

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 __str__(self):
  return self.name

class Tag(models.Model):
 '''標簽'''

 name = models.CharField(max_length=100)

 def __str__(self):
  return self.name

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)

 def __str__(self):
  return self.title
__str__

定義好 __str__ 方法后,解釋器顯示的內容將會是 __str__ 方法返回的內容。這里 Category 返回分類名 name ,Tag 返回標簽名,而 Post 返回它的 title

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

 Python+Django搭建自己的blog網站

可以看到返回的是我們之前存入的數據。

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

 五、博客首頁視圖

5.1.Django處理HTTP請求

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

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

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

Django 如何接收 HTTP 請求? Django 如何處理這個 HTTP 請求? 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>
 <h2>{{ welcome }}</h2>
</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 級別的視圖函數,我們需要編寫真正的首頁視圖函數,當用戶訪問我們的博客首頁時,他將看到我們發表的博客文章列表。像前面演示的那樣

 Python+Django搭建自己的blog網站

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 文件夾連同里面的全部文件一同拷貝進這個目錄。目錄結構

Python+Django搭建自己的blog網站

用下載的博客模板中的 index.html 文件替換掉之前我們自己寫的 index.html 文件。如果你好奇,現在就可以運行開發服務器,看看首頁是什么樣子。

 Python+Django搭建自己的blog網站

如圖所示,你會看到首頁顯示的樣式非常混亂,原因是瀏覽器無法正確加載 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" 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 標簽的 href 屬性里,而 JavaScript 文件的路徑在 script 標簽的 src 屬性里。可以看到諸如 `href="css/bootstrap.min.css" rel="external nofollow" rel="external nofollow" 或者 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' %}" 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>

<!-- 其它內容 -->

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

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

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

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

<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 %} 標簽的作用實際就是把后面的字符串加了一個 /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" rel="external nofollow" rel="external nofollow" rel="external nofollow" >

我們沒有使用模板標簽,因為這里的引用的文件是一個外部文件,不是我們項目里 static\blog\css\ 目錄下的文件,因此無需使用模板標簽。

正確引入了靜態文件后樣式顯示正常了。

Python+Django搭建自己的blog網站

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

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

下面這 5 個 span 標簽里分別顯示了分類(category)、文章發布時間、文章作者、評論數、閱讀量。

<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" >追夢人物</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 評論</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>

再次替換掉一些數據,由于評論數和閱讀量暫時沒法替換,因此先留著,我們在之后實現了這些功能后再來修改它,目前只替換分類、文章發布時間、文章作者:

<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 評論</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 標簽里顯示的是摘要

<div class="entry-content clearfix">
 <p>免費、中文、零基礎,完整的項目,基于最新版 Django 1.10 和 Python 3.5。帶你從零開始一步步開發屬于自己的博客網站,幫助你以最快的速度掌握 Django
 開發的技巧...</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">繼續閱讀 <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">繼續閱讀 <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 后臺登錄頁面,輸入剛才創建的管理員賬戶密碼就可以登錄到后臺了。

Python+Django搭建自己的blog網站

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

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

Python+Django搭建自己的blog網站

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 列表頁面,可以看到顯示的效果好多了。

Python+Django搭建自己的blog網站

八、博客文章詳情頁

 首頁展示的是所有文章的列表,當用戶看到感興趣的文章時,他點擊文章的標題或者繼續閱讀的按鈕,應該跳轉到文章的詳情頁面來閱讀文章的詳細內容。現在讓我們來開發博客的詳情頁面,有了前面的基礎,開發流程都是一樣的了:首先配置 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'^$',views.index,name='index'),
 url(r'^post/(?P<pk>[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 __str__(self):
  return self.title

 # 自定義 get_absolute_url 方法
 # 記得從 django.urls 中導入 reverse 函數
 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') ,我們設定的 name='detail' 在這里派上了用場。看到這個 reverse 函數,它的第一個參數的值是 'blog:detail',意思是 blog 應用下的 name=detail 的函數,由于我們在上面通過 app_name = 'blog' 告訴了 Django 這個 URL 模塊是屬于 blog 應用的,因此 Django 能夠順利地找到 blog 應用下 name 為 detail 的視圖函數,于是 reverse 函數會去解析這個視圖函數對應的 URL,我們這里 detail 對應的規則就是 post/(?P<pk>[0-9]+)/這個正則表達式,而正則表達式部分會被后面傳入的參數 pk 替換,所以,如果 Post 的 id(或者 pk,這里 pk 和 id 是等價的) 是 255 的話,那么 get_absolute_url 函數返回的就是 /post/255/ ,這樣 Post 自己就生成了自己的 URL。

8.2.編寫detail視圖函數

 接下來就是實現我們的 detail 視圖函數了:

# 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})

視圖函數很簡單,它根據我們從 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">
      <h2 class="entry-title">
       <a href="{{ post.get_absolute_url }}" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" >{{ post.title }}</a>
      </h2>
      <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 評論</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">繼續閱讀 <span class="meta-nav">→</span></a>
      </div>
     </div>
    </article>

這里我們修改兩個地方,第一個是文章標題處:

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

我們把 a 標簽的 href 屬性的值改成了 {{ post.get_absolute_url }}。回顧一下模板變量的用法,由于 get_absolute_url 這個方法(我們定義在 Post 類中的)返回的是 post 對應的 URL,因此這里 {{ post.get_absolute_url }} 最終會被替換成該 post 自身的 URL。

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

<a href="{{ post.get_absolute_url }}" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" 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="#" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" 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>
  <span class="current">第 6 頁 / 共 11 頁</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" >下一頁</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">
  <h4 class="widget-title">文章目錄</h4>
  <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" >教程特點</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>
   <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 標簽下的一些內容,讓其顯示文章的實際數據:

<article class="post post-{{ post.pk }}">
 <header class="entry-header">
 <h2 class="entry-title">{{ post.title }}</h2>
 <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 評論</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>

再次從首頁點擊一篇文章的標題或者繼續閱讀按鈕跳轉到詳情頁面,可以看到預期效果了!

Python+Django搭建自己的blog網站

 九、支持Markdown語法和代碼高亮

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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

射阳县| 陵川县| 内乡县| 项城市| 威远县| 从江县| 上蔡县| 泉州市| 黄山市| 西乌珠穆沁旗| 彭水| 弥渡县| 高青县| 新昌县| 梓潼县| 保德县| 邛崃市| 清苑县| 松滋市| 游戏| 南汇区| 双流县| 鹿泉市| 桐城市| 浏阳市| 潢川县| 眉山市| 鄢陵县| 深圳市| 莱州市| 静安区| 长春市| 甘谷县| 色达县| 定南县| 北宁市| 宾川县| 茌平县| 吴桥县| 高要市| 喀喇|