1. 程式人生 > 實用技巧 >通過建立部落格學習Django-1

通過建立部落格學習Django-1

本篇文章主要對追夢人物的部落格:使用 django 開發一個個人部落格進行總結

詳情參考:https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/



部署環境

https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/59/

安裝Python

當前python版本為3.6.4

訪問Python官網進行Python版本的下載,下載具體步驟可參考python安裝過程或者百度一下找到合適的教程

使用虛擬環境

當前使用的是 Pipenv 建立和管理虛擬環境

  • 安裝 Pipenvpip install pipenv
  • 建立資料夾作為專案的根目錄,在當前資料夾執行pipenv install
  • 啟用虛擬環境,在專案根目錄下執行pipenv shell命令

安裝Django

當前使用Django版本為2.2.3

在專案根目錄下執行

pipenv install django==2.2.3

測試安裝是否成功,在專案根目錄下輸入命令

> pipenv run python
(HelloDjango-blog-tutorial-VDQF8f6V) > python
Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:54:40) [MSC v.1900 64 bit (AMD6Type "help", "copyright", "credits" or "license" for more information.
 
>>> import django
>>> print(django.get_version())
2.2.3

安裝好Django後,推薦使用pycharm進行操作

建立Django工程

pipenv run django-admin startproject blogproject C:\Users\yangxg\SpaceLocal\Workspace\G_Courses\HelloDjango-blog-tutorial

django-admin startproject 命令用來初始化一個 django 專案,接收兩個引數,第一個是專案名 blogproject,第二個指定專案生成的位置,因為之前我們為了使用 Pipenv 建立了專案根目錄,所以將專案位置指定為此前建立的位置。

Hello Django

在專案根目錄下執行 pipenv run python manage.py runserver 命令就可以在本機上開啟一個 Web 伺服器

> pipenv run python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
July 05, 2019 - 21:05:37
django version 2.2.3, using settings 'blogproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

結束命令 按Ctrl+z後,再點選Enter結束,或者按 Ctrl+c


部落格應用

https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/60/

建立部落格應用

專案根目錄下執行以下命令建立blog應用

pipenv run python manage.py startapp blog

應用的目錄結構

檢視根目錄新增的blog應用目錄

blog\
    __init__.py
    admin.py
    apps.py
    migrations\
        __init__.py
    models.py
    tests.py
    views.py

將 blog 應用新增settings.py檔案中

HelloDjango-blog-tutorial/blogproject/settings.py
 
## 其他配置項...
 
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog', # 註冊 blog 應用
]
 
## 其他配置項...

建立 Django 部落格的資料庫模型

https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/61/

設計部落格的資料庫表結構

分類和標籤的資料庫表

分類 id 分類名
1 Django
2 Python
標籤 id 標籤名
1 Django 學習
2 Python 學習

編寫部落格模型程式碼

在models.py編寫文章(Post)、分類(Category)以及標籤(Tag)對應的 Python 類

blog/models.py
 
from django.db import models
from django.contrib.auth.models import User
 
# 分類 -----------------------------------------------------------------
class Category(models.Model):
    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)
    
    # 文章正文
    body = models.TextField()
    
    # 建立時間和修改時間
    created_time = models.DateTimeField()
    modified_time = models.DateTimeField()

    # 文章摘要
    excerpt = models.CharField(max_length=200, blank=True)
    
    # 分類和標籤
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    tags = models.ManyToManyField(Tag, blank=True)

    # 文章作者
    author = models.ForeignKey(User, on_delete=models.CASCADE)

部落格模型程式碼程式碼詳解

Category 和 Tag 類

  • CategoryTag 類,都繼承 models.Model 類(django 規定)
  • CategoryTag 類都有一個name 屬性,用來儲存它們的名稱
  • 使用 CharField 指定 name 的資料型別為字串
  • max_length 引數指定 name 允許的最大長度

Post類

  • 必須繼承自 models.Model

  • title:文章的標題,資料型別是 CharField,允許的最大長度 max_length = 70

  • body:文章正文,使用 TextField。使用 CharField儲存比較短的字串,使用 TextField 來儲存大段文字

  • created_timemodified_time:文章的建立時間和最後一次修改時間,儲存時間的列用 DateTimeField 資料類

  • excerpt:文章摘要,可以沒有文章摘要,但預設情況下 CharField 要求我們必須存入資料,否則就會報錯。指定 CharFieldblank=True 引數值後就可以允許空值

  • categorytags:分類與標籤,

    一篇文章只能對應一個分類,但是一個分類下可以有多篇文章,使用 ForeignKey,一對多的關聯關係,ForeignKey 必須傳入一個 on_delete 引數用來指定當關聯的資料被刪除時,被關聯的資料的行為(假定當某個分類被刪除時,該分類下全部文章也同時被刪除),使用 models.CASCADE 引數,意為級聯刪除。

    一篇文章可以有多個標籤,同一個標籤下也可能有多篇文章,使用 ManyToManyField,多對多的關聯關係。同時我們規定文章可以沒有標籤,因此為標籤 tags 指定了 blank=True

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

更多欄位型別查詢: django 官方文件

理解多對一和多對多兩種關聯關係

ForeignKey

文章 ID 標題 正文 分類 ID
1 title 1 body 1 1
2 title 2 body 2 1
3 title 3 body 3 1
4 title 4 body 4 2
分類 ID 分類名
1 Django
2 Python

ManyToManyField

文章 ID 標題 正文
1 title 1 body 1
2 title 2 body 2
3 title 3 body 3
4 title 4 body 4
標籤 ID 標籤名
1 Django 學習
2 Python 學習
文章 ID 標籤 ID
1 1
1 2
2 1
3 2

Django 遷移、操作資料庫

https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/62/

遷移資料庫

切換到 manage.py 檔案所在的目錄(專案根目錄)下,分別執行 pipenv run python manage.py makemigrationspipenv run python manage.py migrate 命令:

> pipenv run python manage.py makemigrations
Migrations for 'blog':
  blog\migrations\0001_initial.py
    - Create model Category
    - Create model Tag
    - Create model Post
 
> pipenv run python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying blog.0001_initial... OK
  Applying sessions.0001_initial... OK

執行 python manage.py makemigrations 後,django 在 blog 應用的 migrations 目錄下生成了一個 0001_initial.py 檔案,是 django 用來記錄對模型修改情況的檔案。目前我們在 models.py 檔案裡建立了 3 個模型類,django 把這些變化記錄在了 0001_initial.py 裡

選擇資料庫版本

在沒有安裝任何的資料庫軟體情況下,django 幫我們遷移了資料庫。這是因為我們使用了 Python 內建的 SQLite3 資料庫,專案根目錄下多出了一個 db.sqlite3 的檔案

django 在 settings.py 裡為我們做了一些預設的資料庫配置:

blogproject/settings.py
 
## 其它配置選項...
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
## 其它配置選項...

用 django 的方式操作資料庫 CURD

存資料

建立一個分類和一個標籤:

>>> from blog.models import Category, Tag, Post
>>> c = Category(name='category test')
>>> c.save()
>>> t = Tag(name='tag test')
>>> t.save()
  • 匯入 3 個之前寫好的模型類
  • 例項化了一個 Category 類和一個 Tag 類,為他們的屬性 name 賦值
  • 呼叫例項的 save 方法讓 django 把這些資料儲存進資料庫

建立User:

User 用於指定文章的作者,執行 pipenv run python manage.py createsuperuser 命令並根據提示建立使用者:

> pipenv run python manage.py createsuperuser
 
使用者名稱 (leave blank to use 'yangxg'): admin
電子郵件地址: [email protected]
Password:                          # 密碼輸入過程中不會有任何字元顯示
Password (again):
Superuser created successfully.

建立一篇文章:

執行 python manage.py shell 進入 Python 命令互動欄,開始建立文章:

>>> from blog.models import Category, Tag, Post
>>> from django.utils import timezone
>>> from django.contrib.auth.models import User
 
>>> user = User.objects.get(username='myuser')
>>> c = Category.objects.get(name='category test')
 
>>> p = Post(title='title test', body='body test', created_time=timezone.now(), modified_time=timezone.now(), category=c, author=user)
>>> p.save()
  • 重啟了 shell,需要重新匯入 CategoryTagPost 以及 User
  • 匯入輔助模組 timezone,呼叫 now() 方法為 created_timemodified_time 指定時間, now 方法返回當前時間
  • 根據使用者名稱和分類名,通過 get 方法取出了存在資料庫中的 UserCategory
  • 為文章指定 titlebodycreated_timemodified_time值,並把它和前面建立的 Category 以及 User 關聯起來
  • 允許為空 excerpttags 沒有為它們指定值

取資料

>>> Category.objects.all()
<QuerySet [<Category: Category object>]>
>>> Tag.objects.all()
<QuerySet [<Tag: Tag object>]>
>>> Post.objects.all()
<QuerySet [<Post: Post object>]>
>>>
  • objects 模型管理器,使用 all 方法,表示把對應的資料全部取出來

為顯示資料更加人性化,將3個模型分別增加一個 __str__ 方法:

blog/models.py
 
class Category(models.Model):
    ...
 
    def __str__(self):
        return self.name
 
class Tag(models.Model):
    ...
 
    def __str__(self):
        return self.name
 
class Post(models.Model):
    ...
 
    def __str__(self):
        return self.title
  • 定義好後Category 返回分類名 nameTag 返回標籤名,Post 返回它的 title

重新執行 python manage.py shell 進入 Shell:

>>> from blog.models import Category, Tag, Post
>>> Category.objects.all()
<QuerySet [<Category: category test>]>
 
>>> Tag.objects.all()
<QuerySet [<Tag: tag test>]>
 
>>> Post.objects.all()
<QuerySet [<Post: title test>]>
 
>>> Post.objects.get(title='title test')
<Post: title test>
  • all 方法返回全部資料,是一個類似於列表的資料結構(QuerySet)
  • get 返回一條記錄資料,如有多條記錄或者沒有記錄,get 方法均會丟擲相應異常

改資料

>>> c = Category.objects.get(name='category test')
>>> c.name = 'category test new'
>>> c.save()
>>> Category.objects.all()
<QuerySet [<Category: test category new>]>
  • 通過 get 方法根據分類名 name 獲取值為 category test 到分類
  • 修改它的 name 屬性為新的值 category test new
  • 呼叫 save 方法把修改儲存到資料庫
  • 檢視資料庫返回的資料已經是修改後的值(TagPost 的修改同category

刪資料

>>> p = Post.objects.get(title='title test')
>>> p
<Post: title test>
>>> p.delete()
(1, {'blog.Post_tags': 0, 'blog.Post': 1})
>>> Post.objects.all()
<QuerySet []>
  • 根據標題 title 的值從資料庫中取出 Post,儲存在變數 p
  • 呼叫它的delete 方法,
  • 檢視Post.objects.all() 返回了一個空的 QuerySet(類似於一個列表),表明資料庫中已經沒有 Post,Post 已經被刪除了

Django 的接客之道

https://www.zmrenwu.com/courses/hellodjango-blog-tutorial/materials/63/

Django 處理 HTTP 請求

瀏覽器知道訪問指定網址後,在後臺主要把我們的訪問意圖包裝成一個 HTTP 請求,發給我們想要訪問的網址所對應的伺服器。

通俗點說就是瀏覽器幫我們通知網站的伺服器,說有人來訪問你啦,訪問的請求都寫在 HTTP 報文裡了,你按照要求處理後告訴我,我再幫你迴應他

Hello 檢視函式

繫結 URL 與檢視函式

在 blog 應用的目錄下建立一個 urls.py 檔案,目錄為:

blog\
    __init__.py
    admin.py
    apps.py
    migrations\
        0001_initial.py
        __init__.py
    models.py
    tests.py
    views.py
    urls.py

在 blog\urls.py 中寫入:

from django.urls import path
 
from . import views
 
urlpatterns = [
    path('', views.index, name='index'),
]
  • 從 django.urls 匯入了 path 函式,從當前目錄下匯入了 views 模組
  • 把網址和處理函式的關係寫在了 urlpatterns 列表裡

繫結關係的寫法:

  • 把網址和對應的處理函式作為引數傳給 path 函式(第一個引數是網址,第二個引數是處理函式),
  • 還傳遞另一個引數 name,引數的值將作為處理函式 index 的別名
  • 當用戶輸入開發的網址 http://127.0.0.1:8000 後,django 首先會把協議 http、域名 127.0.0.1 和埠號 8000 去掉,此時只剩下一個空字串,'' 的模式正是匹配一個空字串,於是二者匹配,django 便會呼叫其對應的 views.index 函式

編寫檢視函式

檢視函式定義在 views.py 檔案裡:

blog/views.py
 
from django.http import HttpResponse
 
def index(request):
    return HttpResponse("歡迎訪問我的部落格首頁!")

Web 伺服器的作用:接收來自使用者的 HTTP 請求,根據請求內容作出相應的處理,並把處理結果包裝成 HTTP 響應返回給使用者

  • 接收 request 的引數(request 是 django 為我們封裝好的 HTTP 請求,它是類 HttpRequest 的一個例項)
  • 返回 HTTP 響應給使用者(HTTP 響應也是 django 幫我們封裝好的,它是類 HttpResponse 的一個例項,只是我們給它傳了一個自定義的字串引數)

配置專案 URL

將 blog 應用下的 urls.py 檔案包含到 blogproject\urls.py 裡去,開啟這個檔案看到如下內容:

blogproject/urls.py
 
"""
一大段註釋
"""
 
from django.contrib import admin
from django.urls import path
 
urlpatterns = [
    path('admin/', admin.site.urls),
]

修改成如下的形式:

from django.contrib import admin
from django.urls import path, include
 
urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('blog.urls')),
]
  • 匯入include 函式,把 blog 應用下的 urls.py 檔案包含進來
  • include 前有一個 '',這是一個空字串。這裡也可以寫其它字串,django 會把這個字串和後面 include 的 urls.py 檔案中的 URL 拼接

執行結果

執行 pipenv run python manage.py runserver 開啟開發伺服器

歡迎訪問我的部落格首頁!

使用 django 模板系統

  • 根目錄下建立一個 templates 資料夾,用來存放模板
  • 在 templates 目錄下建立 blog 資料夾,用來存放和 blog 應用相關的模板
  • 在 templates\blog 目錄下建立一個名為 index.html 的檔案
HelloDjango-blog-tutorial\
    manage.py
    ...
    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>
  • {{ title }}{{ welcome }}:用 {{ }} 包起來的變數叫做模板變數
  • django 在渲染這個模板的時候會根據我們傳遞給模板的變數替換掉這些變數,最終在模板中顯示的將會是我們傳遞的值

在 settings.py 檔案裡設定模板檔案所在的路徑:

blogproject/settings.py
 
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.djangoTemplates',
        'DIRS': [],
        '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',
            ],
        },
    },
]

DIRS 設定模板的路徑,在 [] 中寫入 os.path.join(BASE_DIR, 'templates')

blogproject/settings.py
 
TEMPLATES = [
    {
        ...
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        ...
    },
]
  • BASE_DIR 是 settings.py 在配置開頭前面定義的變數,記錄的是工程根目錄 HelloDjango-blog-tutorial 的值,在這個目錄下有模板檔案所在的目錄 templates\
  • os.path.joinBASE_DIRtemplates兩個路徑連線,構成完整的模板路徑,django 就知道去這個路徑下面找我們的模板了

修改檢視函式:

blog/views.py
 
from django.shortcuts import render
 
 
def index(request):
    return render(request, 'blog/index.html', context={
        'title': '我的部落格首頁',
        'welcome': '歡迎訪問我的部落格首頁'
    })

不直接把字串傳給 HttpResponse ,而是呼叫render 函式,根據傳入的引數來構造 HttpResponse

  • 把 HTTP 請求傳進去
  • render 根據第二個引數的值 blog/index.html 找到這個模板檔案並讀取模板中的內容
  • render 根據傳入的 context 引數的值把模板中的變數替換為我們傳遞的變數的值
  • {{ title }} 被替換成了 context 字典中 title 對應的值,{{ welcome }} 也被替換成相應的值
  • HTML 模板中的內容字串被傳遞給 HttpResponse 物件並返回給瀏覽器

部落格從“裸奔”到“有面板”

首頁檢視函式

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', context={'post_list': post_list})
  • 使用 all() 方法從資料庫裡獲取了全部的文章,存在了 post_list 變數裡
  • all 方法返回的是一個 QuerySet(可以理解成一個類似於列表的資料結構)
  • order_by 方法對這個返回的 QuerySet進行排序
  • 排序依據的欄位是 created_time,即文章的建立時間
  • - 號表示逆序,如果不加 - 則是正序
  • 渲染了 blog\index.html 模板檔案,並且把包含文章列表資料的 post_list 變數傳給了模板

處理靜態檔案

部落格模板下載

在 blog 應用下建立 static 資料夾,為避免衝突,在 static 目錄下建立 blog 資料夾,把下載的部落格模板的全部檔案拷貝進這個目錄

blog\
    __init__.py
    static\
        blog\
            css\
                .css 檔案...
            js\
                .js 檔案...
    admin.py
    apps.py
    migrations\
        __init__.py
    models.py
    tests.py
    views.py

用下載的部落格模板中的 index.html 檔案替換掉之前我們自己寫的 index.html 檔案

正確引入 static 檔案下的 CSS 和 JavaScript 檔案:

templates/blog/index.html
 
+ {% load static %}
<!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="css/bootstrap.min.css">
      <link rel="stylesheet" href="http://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
      - <link rel="stylesheet" href="css/pace.css">
      - <link rel="stylesheet" href="css/custom.css">
      + <link rel="stylesheet" href="{% static 'blog/css/bootstrap.min.css' %}">
      + <link rel="stylesheet" href="{% static 'blog/css/pace.css' %}">
      + <link rel="stylesheet" href="{% static 'blog/css/custom.css' %}">
 
      <!-- js -->
      - <script src="js/jquery-2.1.3.min.js"></script>
      - <script src="js/bootstrap.min.js"></script>
      - <script src="js/pace.min.js"></script>
      - <script src="js/modernizr.custom.js"></script>
      + <script src="{% static 'blog/js/jquery-2.1.3.min.js' %}"></script>
      + <script src="{% static 'blog/js/bootstrap.min.js' %}"></script>
      + <script src="{% static 'blog/js/pace.min.js' %}"></script>
      + <script src="{% static 'blog/js/modernizr.custom.js' %}"></script>
  </head>
  <body>
      <!-- 其它內容 -->
      - <script src="js/script.js' %}"></script>
      + <script src="{% static 'blog/js/script.js' %}"></script>
  </body>
</html>

- 表示刪掉這一行, + 表示增加這一行

  • {% %} 包裹起來的叫做模板標籤,功能類似於函式,例如這裡的 static 模板標籤,它把跟在後面的字串 'css/bootstrap.min.css' 轉換成正確的檔案引入路徑
  • {{ }} 包裹起來的叫做模板變數,作用是在最終渲染的模板裡顯示由檢視函式傳過來的變數值

修改模板

在模板 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>
...

使用 {% 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 %}
...
  • {% empty %} 的作用是當 post_list 為空,即資料庫裡沒有文章時顯示 {% empty %} 下面的內容
  • {% endfor %} 告訴 django 迴圈在這裡結束了

在迴圈體內通過 post 變數訪問單篇文章的資料

<h1 class="entry-title">
    <a href="single.html">Adaptive Vs. Responsive Layouts And Optimal Text Readability</a>
</h1>
<div class="entry-meta">
  <span class="post-category"><a href="#">django 部落格教程</a></span>
  <span class="post-date"><a href="#"><time class="entry-date"
                                            datetime="2012-11-09T23:15:57+00:00">2017年5月11日</time></a></span>
  <span class="post-author"><a href="#">追夢人物</a></span>
  <span class="comments-link"><a href="#">4 評論</a></span>
  <span class="views-count"><a href="#">588 閱讀</a></span>
</div>
<!-- 摘要 -->
<div class="entry-content clearfix">
  <p>免費、中文、零基礎,完整的專案,基於最新版 django 1.10 和 Python 3.5。帶你從零開始一步步開發屬於自己的部落格網站,幫助你以最快的速度掌握 django
    開發的技巧...</p>
  <div class="read-more cl-effect-14">
    <a href="#" class="more-link">繼續閱讀 <span class="meta-nav">→</span></a>
  </div>
</div>

替換成 posttitle 屬性值

<h1 class="entry-title">
    <a href="single.html">{{ post.title }}</a>
</h1>
<div class="entry-meta">
  <span class="post-category"><a href="#">{{ post.category.name }}</a></span>
  <span class="post-date"><a href="#"><time class="entry-date"
                                            datetime="{{ post.created_time }}">{{ post.created_time }}</time></a></span>
  <span class="post-author"><a href="#">{{ post.author }}</a></span>
  <span class="comments-link"><a href="#">4 評論</a></span>
  <span class="views-count"><a href="#">588 閱讀</a></span>
</div>
<!-- 摘要 -->

<div class="entry-content clearfix">
  <p>{{ post.excerpt }}</p>
  <div class="read-more cl-effect-14">
    <a href="#" class="more-link">繼續閱讀 <span class="meta-nav">→</span></a>
  </div>
</div>