---
# System prepended metadata

title: Django

---

---
title: 'Django'
disqus: 'For ID student'
---

Django For ID student
===
## Contents

[TOC]

## Django ID學習指南

這份學習指南適合所有 Django 初學者，為了更好的學習效果，我們希望你能具備：
* Web 的初步認識
* 了解如何使用 Command Line
* 略懂 Python 基礎語法
* 看得懂簡單的 HTML / CSS

### 學習前準備
這此教學使用
* https://repl.it
### 學習範例
透過這份學習指南，你會學習到 Django 的程式架構，從創建一個專案，到最後將網站發佈到網路上，建立一個屬於自己的旅遊日記。
## Django 介紹
Django (/ˈdʒæŋɡoʊ/ jang-goh) 可以說是 Python 最著名的 Web Framework，一些知名的網站如 Pinterest, Instagram, Disqus 等等都使用過它來開發。

它有以下的特色：
* 免費開放原始碼
* 著重快速開發、高效能
* 遵從 DRY ( Don't Repeat Yourself ) 守則，致力於淺顯易懂和優雅的程式碼
* 使用類似 Model–view–controller (MVC) pattern 的架構
### Web Framework
Web framework，簡單來說就是當你開發 Web 應用程式時所用的框架。它通常會提供：
1. 一個既定的程式骨架 -- 你必須按照它的規範寫程式，例如把資料庫相關的程式與跟畫面溝通的程式分開，而不是全部寫在同一個檔案。這對於程式的開發速度、再利用性、和程式可讀性等等都有相當大的好處。
2. 強大且豐富的函式庫 ( Libraries ) -- 通常會提供一些開發網站所需要且常用的功能，例如使用者認證、安全機制、URL mapping、資料庫連接等等。讓你在開發網站時可以直接使用函式庫，然後專注在客製化自己的功能。
### Django 的 架構
如同一些比較著名的 Web framework，Django 同樣也使用了類似 MVC 的架構，只是在定義和解釋上略為不同，稱為 MTV ( Model–Template–View )，我們可以透過下面這張圖來了解其運作方式：
![](https://i.imgur.com/1PdOgPA.png)
## Django 安裝
1. https://repl.it
2. 點選new repl
3. 選擇 Django
## Django 安裝確定
案F1 或 Ctrl + shift + P 選擇Open Shell
請在虛擬環境下指令輸入 python，進入互動式命令列環境
```
>>>python
```
輸入以下的指令取得 Django 版本資訊：
```
>>> import django
>>> django.VERSION
```

如果看見類似上面的訊息，就代表安裝成功囉！
## Django Project and apps
每一個 Django project 裡面可以有多個 Django apps，可以想成是類似模組的概念。在實務上，通常會依功能分成不同 app，方便未來的維護和重複使用。
例如，我們要做一個類似 Facebook 這種網站時，依功能可能會有以下 apps：
* 使用者管理 -- accounts
* 好友管理 -- friends
* 塗鴉牆管理 -- timeline
* 動態消息管理 -- news
若未來我們需要寫個購物網站，而需要會員功能時，accounts app（使用者管理）就可以被重複使用。
---
這一章，你會學到如何使用 Django 命令列工具建立 Django project 和一個 Django app。

---
### 建立 Django project(replt.it已經幫你弄好了)
首先，使用 django-admin.py 來建立第一個 Django project mysite:
```
~/djangogirls$ django-admin.py startproject mysite
```
此時會多了一個 mysite 資料夾。我們切換進去：
```
~/djangogirls$ cd mysite
```
startproject 這個 Django 指令除了建立專案資料夾，也預設會建立一些常用檔案，你可以使用 ls 或 dir /w (Windows) 檢視檔案結構。

目前 project 的檔案結構如下:
![](https://i.imgur.com/4pVkj0P.png)

---

### 建立 Django application（app）(replt.it已經幫你弄好了)
讓我們利用 startapp 建立第一個 Django app -- trips:
(djangogirls_venv) ~/djangogirls/mysite$ python manage.py startapp main
startapp 會按照你的命名建立一個同名資料夾和 app 預設的檔案結構如下：
```
main
├── __init__.py
├── admin.py
├── migrations
├── models.py
├── tests.py
└── views.py
```
將新增的 Django app 加入設定檔
在前一個指令，我們透過 Django 命令列工具建立了 trips 這個 app。但若要讓 Django 知道要管理哪些 apps，還需再調整設定檔。
新增 app
打開 mysite/settings.py，找到 INSTALLED_APPS，調整如下：
```
# mysite/settings.py
# Application definition

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'main',
)
```
請注意 app 之間有時候需要特定先後順序。在此，我們將自訂的 trips 加在最後面。

---
#### 預設安裝的 Django app
Django 已將常用的 app 設定為 INSTALLED_APPS 。例如，auth（使用者認證）、admin （管理後台）... 等等，我們可依需求自行增減。

---
## Views and URLconfs
![](https://i.imgur.com/jIWeVHK.png)
在前面的介紹，我們有提到 Django 的 MTV 架構。其處理 request 的流程如下：
1. 瀏覽器送出 HTTP request
2. Django 依據 URL configuration 分配至對應的 View
3. View 進行資料庫的操作或其他運算，並回傳 HttpResponse 物件
4. 瀏覽器依據 HTTP response 顯示網頁畫面

---
這一章，我們將透過 Hello World 範例 ，瞭解 Django 如何處理一個 request 的流程。

---
### Django Views
Django view 其實是一個 function，處理 HttpRequest 物件，並回傳 HttpResponse 物件，大致說明如下：
* 會收到 HttpRequest 參數： Django 從網頁接收到 request 後，會將 request 中的資訊封裝產生一個 HttpRequest 物件，並當成第一個參數，傳入對應的 view function。
* 需要回傳 HttpResponse 物件： HttpResponse 物件裡面包含：
  * HttpResponse.contentHttp
  * Response.status_code …等
### 建立第一個 View
首先建立一個名為 hello_world 的 view。
在 main/views.py 輸入下列程式碼：
```
# main/views.py

from django.http import HttpResponse


def hello_world(request):
    return HttpResponse("Hello World!")
```
以上程式在做的事就是：
1. 從 django.http 模組中引用 HttpResponse 類別
2. 宣告 hello_world 這個 view
3. 當 hello_world 被呼叫時，回傳包含字串 Hello World! 的 HttpResponse 物件。
### Django URL 設定
最後，Django 需要知道 URL 與 view 的對應關係。
例如：

有人瀏覽 http://127.0.0.1:8000/hello/ 時 ，hello_world() 這個 view function 需要被執行。
而這個對應關係就是 URL conf (URL configuration)。

---
### URL Conf
* 通常定義在 urls.py
* 是一連串的規則 (URL patterns)
* Django 收到 request 時，會一一比對 URL conf 中的規則，決定要執行哪個 view function
---
現在我們來設定 Hello World 範例的 URL conf
首先打開 main/urls.py，先 import 剛剛寫的 view function：
```
from main import views
```
然後在 urlpatterns 中加入下面這行：
```
url('hello/',views.hello_world),
```
現在 main/urls.py 的內容應該會像下面這樣：
```
# main/urls.py

from django.conf.urls import url
from django.contrib import admin
from main import views

urlpatterns = [
  url(r'^admin/', admin.site.urls),
  url(r'^$', views.home, name='home'),
  url('hello/',views.hello_world),
]
```
以上程式透過 url() function 傳入兩個參數 regex, view：
```
url(regex, view)
```
* regex -- 定義的 URL 規則
  * 規則以 regular expression（正規表示式）來表達
  * 'hello/' 代表的是 hello/ 這種 URL
* view -- 對應的 view function
  * 指的是 hello_world 這個 view
### 測試 Hello World
## Templates
上一章的例子，只是很簡單的顯示一行字串。 現在，讓我們加上一些 HTML/CSS 美化網頁，並動態顯示每次進來這個頁面的時間。
### 第一個 Template
實務上，我們會將前端的程式碼獨立出來，放在 templates 資料夾裡。不僅增加可讀性，也方便與設計師或前端工程師分工。
### Template 資料夾(replt.it已經幫你弄好了)
首先建立 Template 資料夾。開啟終端機 (如果不想關閉 web server，可以再開新一個新的終端機視窗)，並確認目前所在位置為 mysite/。
新增一個名為 templates 的資料夾`：
```
~/djangogirls/mysite$ mkdir templates
``` 
### 設定 Templates 資料夾的位置 (replt.it已經幫你弄好了)
建立好資料夾以後，我們需要修改 main/settings.py 中的 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路徑
```
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
```
好讓 Django 找得到剛剛建立的 templates 資料夾。
### 建立第一個 Template
[html簡易教學](https://djangogirlstaipei.herokuapp.com/tutorials/html/?os=windows)
新增檔案 templates/hello_world.html：
![](https://i.imgur.com/1niJSRD.png)
並將下列的 HTML 複製到 hello_world.html：
```
<!-- hello_world.html -->

<!DOCTYPE html>
<html>
    <head>
        <title>I come from template!!</title>
        <style>
            body {
               background-color: lightyellow;
            }
            em {
                color: LightSeaGreen;
            }
        </style>
    </head>
    <body>
        <h1>Hello World!</h1>
        <em>{{ current_time }}</em>
    </body>
</html>
```
### 在 Template 中顯示變數
以上 Template 中，有個地方要特別注意：
```
<em>{{ current_time }}</em>
```
在 Template 裡面．我們會使用兩個大括號，來顯示變數current_time。

---
{{<variable_name>}} 是在 Django Template 中顯示變數的語法。
其它 Django Template 語法，我們會在後面的章節陸續練習到。

---

### 使用 render function
最後，將 view function hello_world 修改如下：
```
from django.shortcuts import render
from datetime import datetime


# Create your views here.
def home(request):
    return render(request, 'main/index.html')

def hello_world(request):
    return render(request, 'hello_world.html', {
        'current_time': str(datetime.now()),
    })
```
1. 顯示目前時間： 為了顯示動態內容，我們 import datetime 時間模組，並用datetime.now()取得現在的時間。
2. render： 我們改成用 render 這個 function 產生要回傳的 HttpResponse 物件。
這次傳入的參數有：
1. request -- HttpRequest 物件
1. template_name -- 要使用的 template
1. dictionary -- 包含要新增至 template 的變數
---
render：產生 HttpResponse 物件。
render(request, template_name, dictionary)

---
### 大功告成
## Models
現今的網站，都不再只是僅單純展示網頁內容的靜態網頁。大多數網站，都會加上一些與使用者互動的功能，如留言版、討論區、投票等等。而這些使用者產出的資料，往往會儲存於資料庫中。

---
這一章，你會學到如何利用 Django Model 定義資料庫的結構（schema），並透過 Django 指令創建資料庫、資料表及欄位。

---
### 使用 Django Model 的好處
為了開發方便，我們使用 Python 預設的資料庫引擎 - SQLite。打開 main/settings.py，看看 DATABASES 的設定。它應該長得像下面這樣：
```
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
```
在這裡我們設定了資料庫連線的預設值：
* ENGINE -- 你要使用的資料庫引擎。例如：
    * MySQL: django.db.backends.mysql
    * SQLite 3: django.db.backends.sqlite3
    * PostgreSQL: django.db.backends.postgresql_psycopg2
* NAME -- 你的資料庫名稱
如果你使用 MySQL 或 PostgreSQL 等等資料庫，可能還要設定它的位置、名稱、使用者等等。不過我們這裡使用的 SQLite 3 不需要這些性質，所以可以省略。
### Django Models
我們在 main/models.py 宣告一個 Post 類別，並定義裡面的屬性，而 Django 會依據這個建立資料表，以及資料表裡的欄位設定：
```
# main/models.py

from django.db import models


class Post(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField(blank=True)
    photo = models.URLField(blank=True)
    location = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)
```
* Django 預設會為每一個 Model 加上 id 欄位，並將這個欄位設成 primary key（主鍵），簡稱 pk，讓每一筆資料都會有一個獨一無二的 ID。
* 為 Post 定義以下屬性：
![](https://i.imgur.com/CryvxJL.png)
Model fields 可為 Django Model 定義不同型態的屬性。
    * CharField -- 字串欄位，適合像 title、location 這種有長度限制的字串。
    * TextField -- 合放大量文字的欄位
    * URLField -- URL 設計的欄位
    * DateTimeField -- 日期與時間的欄位，使用時會轉成 Python datetime 型別。
    
更多 Model Field 與其參數，請參考 Django 文件

### 同步資料庫
首先執行 makemigrations 指令：
```
python manage.py makemigrations
```
這個指令會根據你對 Model 的修改刪除建立一個新的 migration 檔案，讓 migrate 指令執行時，可以照著這份紀錄更新資料庫。
接著用以下的指令，讓 Django 根據上面的紀錄，把 models.py 中的欄位寫入資料庫：
```
python manage.py migrate
```
migrate 指令會根據 INSTALLED_APPS 的設定，按照 app 順序建立或更新資料表，將你在 models.py 裡的更新跟資料庫同步。
## Admin
大部份網站都設計有管理後台，讓管理者方便新增或異動網站內容。
而這樣的管理後台，Django 也有內建一個 App -- Django Admin 。只需要稍微設定，網站就能擁有管理後台功能。
前一章，我們學到如何使用 Django Model 抽象地表達資料庫結構。現在，我們要透過 Django Admin 看到實際的資料，並跟資料庫進行互動。

---
完成本章後，你會瞭解如何設定 Django Admin，並使用 Django 管理後台，完成 Post 的新增、修改及刪除。

---
### 設定管理後台

### 將 Django Admin 加入 INSTALLED_APPS
後台管理的功能 Django 已預設開啟。因此，設定檔中的 INSTALLED_APPS 裡，已經有 django.contrib.admin 這個 app ：
```
# main/settings.py

INSTALLED_APPS = (
    'django.contrib.admin',
    ...
)
```
當你在同步資料庫時，也會建立需要的資料表及欄位。
### 設定管理後台的 URL
為了讓你可以從瀏覽器進入管理後台，我們需要設定對應的 urls 。
我們將管理後台的網址設定為 /admin/。確認 mysite/urls.py 中的 urlpatterns 包含下面這行：
```
url(r'^admin/', include(admin.site.urls)),
```
### 建立 superuser
要使用 Django 的管理後台，需要一個管理員帳號。
使用 createsuperuser 這個指令，建立一個 superuser：
```
>>>python manage.py createsuperuser
Username (leave blank to use 'YOUR_NAME'):
Email address: your_name@yourmail.com
Password:
Password (again):
Superuser created successfully.
```
輸入帳號、Email、密碼等資訊，就完成 superuser 的新增了。
### 註冊 Model class
最後，我們需要在讓 Django 知道，有哪些 Model 需要管理後台。
修改 main app 裡的 admin.py，並註冊 Post 這個 Model：
```
# main/admin.py

from django.contrib import admin
from .models import Post


admin.site.register(Post)
```
### 使用管理後台
### 進入管理後台
連至 http://127.0.0.1:8000/admin，可以看到管理後台的登入頁面：
![](https://i.imgur.com/t6CUK42.png)


請輸入你剛創立的 superuser 帳號密碼，進入管理後台：
第一個區塊 Authentication and Authorization ，可以管理使用者（User）和 群組（Group）；第二個 Trips 區塊裡，則可以看到剛剛設定的 Post model。在這裡可以執行 Post 的新增、修改、刪除等功能。
![](https://i.imgur.com/RmXu939.png)


新增一個 Post
現在試著建立一個新的 Post 看看：
![](https://i.imgur.com/x6LL6mK.png)


建立成功後會回到 Posts 頁面，你會發現有一筆資料顯示為 Post object：
![](https://i.imgur.com/xgZV2GE.png)

---
Django 通常以 Post object 來表示 Post 物件，但此種顯示不易辨別。我們可以透過 def __str__ 更改 Post 的表示方式。
修改 main/models.py：
```
# main/models.py

from django.db import models


class Post(models.Model):
    ...
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title
```
### 小結
你現在己經學會：
* 設定 Django Admin
* 建立 superuser
* 註冊 Model 至 Admin
## 使用 Django ORM 操作資料庫
在前一章，我們利用 Django Admin 新增、修改及刪除 Post 。而實際在寫程式時，我們會使用 Django 提供的 QuerySet API，來達成類似的資料庫操作。

---
本章你會學到：如何使用 Django QuerySet API 與資料庫互動 (CRUD)。
CRUD 指的是，Create (新增)、Read (讀取)、Update (修改)、Delete (刪除) 等常見的資料庫操作。


---
### 使用 Django Shell
使用 shell 指令，進入 Django Shell：
```
python manage.py shell
```
### QuerySet API
#### Create
首先，讓我們來試著新增幾筆資料：
```
>>> from main.models import Post

>>> Post.objects.create(title='My First Trip', content='肚子好餓，吃什麼好呢?',  location='台北火車站')
<Post: My First Trip>

>>> Post.objects.create(title='My Second Trip', content='去散散步吧',  location='大安森林公園')
<Post: My Second Trip>

>>> Post.objects.create(title='Django 大冒險', content='從靜態到動態',  location='台北市大安區復興南路一段293號')
<Post: Django 大冒險>
```
#### Read
若想顯示所有的 Post ，可以使用 all()：
```
>>> from main.models import Post
>>> Post.objects.all()
[<Post: My First Trip>, <Post: My Second Trip>, <Post: Django 大冒險>]
```
只想顯示部分資料時，則可以使用 get 或 filter：
```
>>> Post.objects.get(pk=1)
<Post: My First Trip>

>>> Post.objects.filter(location__contains='台北')
[<Post: My First Trip>, <Post: Django 大冒險>]
```
* get：返回符合條件的唯一一筆資料。（注意：如果找不到符合條件的資料、或是有多筆資料符合條件，都會產生 exception）
* filter：返回符合條件的陣列。如果找不到任何資料則會返回空陣列。
### Update
當想修改資料時，可以使用 update 更新一筆或多筆資料：
首先，這裡使用 contains 針對title欄位，篩選出所有標題中包含 Trip 字眼的 Post
```
>>> posts = Post.objects.filter(title__contains='Trip')
```

---
注意：Django ORM 會使用雙底線__，來區隔欄位title和篩選方法contains，如果只用一個底線，Django 會因為找不到欄位title_contains而出錯。

---
共有 2 個 Post 符合上面的條件
```
>>> posts
[<Post: My First Trip>, <Post: My Second Trip>]
```
我們將 location 的值印出

```
>>> posts[0].location
'台北火車站'

>>> posts[1].location
'大安森林公園'
```
印出後發現，Post 的 location 分別為'台北火車站'和'大安森林公園'。現在我們試試用 update 指令，把它們改成 '象山親山步道'
```
>>> posts.update(location='象山親山步道')
2
```
回傳的數字 2 指的是已被更新的資料筆數。我們可以驗證一下 location 是否皆已被正確更新
```
>>> posts[0].location
'象山親山步道'

>>> posts[1].location
'象山親山步道'
```
### Delete
我們也可以使用 delete 刪除資料：
我們試著使用 delete，將剛剛的那兩筆 Post 刪除。
```
>>> posts.delete()
```
最後確認一下，資料是否刪除
```
>>> Post.objects.all()
[<Post: Django 大冒險>]
```
## Template tags
在先前的 Templates 章節中，我們已經學會基礎的 Django Template 用法 (在 Template 裡呈現變數內容)。但為了產生完整的網頁，我們會需要能在 Template 裡執行一些簡單的 Python 語法，例如：
* 邏輯判斷 (if-else) -- 若使用者己經登入，則顯示使用者的暱稱；若未登入，則顯示登入按鈕
* 重覆 HTML 片段 (for loop) -- 列出所有好友的帳號和顯示圖片
* 格式化 Template 中的變數 -- 日期的格式化等等

Django template tags 讓你可以在 HTML 檔案裡使用類似 Python 的語法，動態存取從 view function 傳過來的變數，或是在顯示到瀏覽器之前幫你做簡單的資料判斷、轉換、計算等等。

---
在這一章，我們將使用 Django ORM 存取資料庫，撈出旅遊日記全部的 posts 傳入 template，並使用 Django 的 template tags 與 filters，一步步產生旅遊日記的首頁。

---
### 建立旅遊日記的首頁
#### 確認首頁需求
在開始動工之前，我們先確認需求。
旅遊日記的首頁應該會有：
1. 標題
2. 照片
3. 發佈日期
4. 部份的遊記內文
#### 建立首頁的 View
首先我們建立一個新的 view function - home()：
```
# main/views.py

# ...

from django.shortcuts import render
from .models import Post


def home(request):
    post_list = Post.objects.all().reverse()[::-1]
    return render(request, 'home.html', {
        'post_list': post_list,
    })
```
* 匯入所需的 model -- 記得 import 需要用到的 Model Post
* 取得所有 posts -- 透過 Post.objects.all() 從資料庫取得全部的 posts，並傳入 home.html 這個 template。
#### 設定首頁的 URL
接下來，我們修改 urls.py ，將首頁（正規表達式 ^$）指向 home() 這個 view function：
```
url(r'^$', views.home, name='home'),
```
### Template Tags
#### 建立首頁的 Template 並印出 post_list
首先，在 templates 資料夾底下新增 home.html：
```
<!-- home.html -->

{{ post_list }}
```
打開瀏覽器進入首頁 http://127.0.0.1:8000/ ,可以看到 post_list 已呈現至網頁上了。
#### 顯示 Post 中的資料
仔細觀察印出的 post_list，會發現是以 list 的形式顯示。但我們希望的則是：存取每個 Post 中的資料，並印出來。
為了達成這個功能，我們會用到 for 這個 template tag。
##### for 迴圈
在寫 Python 時，若想存取 list 裡的每一個元素，我們會使用 for 迴圈。而在 Django Template 中，也提供了類似的 template tags -- {% for %}。

---
##### {% for %}
在 template 中使用類似 Python 的 for 迴圈，使用方法如下：
```
{% for <element> in <list> %}
    ...
{% endfor %}
```

---
瞭解了 for 的用法後，我們試著印出首頁所需的資訊。修改 home.html 如下：
```
<!-- home.html -->

{% for post in post_list %}
    <div>
    {{ post.title }}
    {{ post.created_at }}
    {{ post.photo }}
    {{ post.content }}
    </div>
{% endfor %}
```
* 開始標籤為 {% for %} 開始；結束標籤為 {% endfor %}
* post_list 中有 3 個元素，所以 for 區塊中的內容會執行 3 次
* 迴圈中，使用標籤 {{ var }}，反覆印出每個 post 中的標題、建立時間、照片網址和文章內容

重新整理瀏覽器，網頁上會有首頁所需的 post 資訊
#### 顯示照片
現在網頁已經有照片網址，我們稍微修改 template ，讓照片以圖片方式呈現。
把 home.html 的下面這一行：
```
{{ post.photo }}
```
換成下面這樣：
```
<div class="thumbnail">
    <img src="{{ post.photo }}" alt="">
</div>
```
#### 處理沒有照片的遊記
##### if…else
另一個常用的 template tags 是 {% if %} 判斷式，用法如下：
```
{% if post.photo %}
<div class="thumbnail">
    <img src="{{ post.photo }}" alt="">
</div>
{% else %}
<div class="thumbnail thumbnail-default"></div>
{% endif %}
```
* 符合條件所想要顯示的 HTML 放在 {% if<condition>%} 區塊裡
* 不符合的則放在 {% else %} 區塊裡面
* 最後跟 for 一樣，要加上 {% endif %} 作為判斷式結尾。

在這裡，我們判斷如果 post.photo 有值就顯示照片，否則就多加上一個 CSS class photo-default 另外處理。

#### Template Filter
除了 template tags ，Django 也內建也許多好用的 template filters。它能在變數顯示之前幫你做計算、設定預設值，置中、或是截斷過長的內容等等。使用方法如下:

{{<variable_name>|<filter_name>:<filter_arguments>}}

* <variable_name> -- 變數名稱
* <filter_name> -- filter 名稱，例如 add、cut 等等
* <filter_arguments> -- 要傳入 filter 的參數
#### 變更時間的顯示格式
在這裡，我們只練習一種很常用的 filter date。它可以將 datetime 型別的物件，以指定的時間格式輸出。
我們試著將 created_at 時間顯示成年 / 月 / 日：
```
{{ post.created_at|date:"Y / m / d" }}
```
### 完整的 HTML 與 CSS
```
<!-- home.html -->

<!DOCTYPE html>
<!-- home.html -->

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>A Django Ncku's Adventure</title>
    <link href="//fonts.googleapis.com/css?family=Lemon" rel="stylesheet" type="text/css">
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
    <link href="//djangogirlstaipei.github.io/assets/css/style.css" rel="stylesheet" type="text/css">
</head>
<body>
    <div class="header">
        <h1 class="site-title text-center">
            <a href="/">A Django Ncku's Adventure</a>
        </h1>
    </div>
    <div class="container">
        {% for post in post_list %}
        <div class="post-wrapper">
            <div class="post">
                <div class="post-heading">
                    <h2 class="title">
                        <a href="#">{{ post.title }}</a>
                    </h2>
                    <div class="date">{{ post.created_at|date:"Y / m / d" }}</div>
                </div>
                {% if post.photo %}
                <div class="thumbnail">
                    <img src="{{ post.photo }}" alt="">
                </div>
                {% else %}
                <div class="thumbnail thumbnail-default"></div>
                {% endif %}
                <div class="post-content read-more-block">
                    {{ post.content }}
                </div>
                <div class="post-footer">
                    <a class="read-more" href="#">
                        Read More <i class="fa fa-arrow-right"></i>
                    </a>
                </div>
            </div>
        </div>
        {% endfor %}
    </div>
</body>
</html>
```
## Dynamic URL
除了在首頁顯示文章的摘要外，通常也會希望每篇文章能有獨立的網址與頁面。例如，我們可能會希望 http://127.0.0.1/post/3/ 能夠是 pk 為 3 那篇文章的網址，而頁面內容則是此篇日記的詳細資訊，而非摘要。

---
在這個章節，我們會學到如何設定動態網址的 URL conf，讓每篇旅遊日記，擁有獨一無二的網址與頁面。

---

### 建立單篇文章的 View
首先建立單篇文章所使用的 view function。在 main/views.py 中，新增 post_detail 這個 view 如下：
```
# main/views.py

# ...

from django.shortcuts import render
from .models import Post


def post_detail(request, pk):
    post = Post.objects.get(pk=pk)
    return render(request, 'post.html', {'post': post})
```
以訪客瀏覽 http://127.0.0.1:8000/post/3/ 的例子，來解釋以上程式：
* 目前瀏覽文章的 pk 會傳入 view 中： 當訪客瀏覽 http://127.0.0.1/post/3/ 時，傳入 view 的 pk 會是 3。
  * URL 與 pk 的對應，會在稍後設定。這裡只需知道 view 中傳入的，會是當前瀏覽文章 pk 即可。
* 取得傳入 pk 的那篇 Post 資料： 當傳入的 pk=3，代表訪客想看到 pk=3 那篇文章。我們可以利用之前學過的 ORM 語法 get， 取得該篇日記的 Post 物件：
```
post = Post.objects.get(pk=pk)  # 此時 pk = 3
```
* 回傳 HttpResponse： 將取得的 post（pk=3）傳入 Template post.html，並呈現 render 後的結果。
### 設定動態網址的對應
日記單頁的 view function 完成後，我們來設定網址與 view 的對應。修改 main/urls.py ，加入以下內容：
```
from django.conf.urls import url
from django.contrib import admin
from main import views

urlpatterns = [
  url(r'^admin/', admin.site.urls),
  url(r'^$', views.home, name='home'),
  url('hello/',views.hello_world),
  url(r'^post/(?P<pk>\d+)/$', views.post_detail, name='post_detail'),
]
```
上面的修改完成後，只要連至http://127.0.0.1/post/3/ 就會對應到 post_detail() 這個 view，並且傳入的 pk=3 。
### 使用 Regex 提取部份 URL 為參數
我們前面提過，Django 的 URL 是一個 regular expression (regex)。Regular expression 語法可用來描述一個字串的樣式。 除了可以表示固定字串之外，還可以用來表示不確定的內容。我們一步一步解釋文章單頁所使用的 URL 設定：
```
(?P<pk>\d+)
```
1. \d 代表一個阿拉伯數字。
1. +代表「一個以上」。
所以 \d+ 代表一個以上的阿拉伯數字，例如「0」、「99」、「12345」。可是像「8a」就不符合，因為「a」不是數字。
1. (?P<pk>) 代表「把這一串東西抓出來，命名為 pk。
所以 (?P<pk>\d+) 代表：抓出一個以上阿拉伯數字，並把抓出來的東西取名為 pk。

綜合以上的規則，r'^post/(?P<pk>\d+)/$' 會達成以下的效果：
![](https://i.imgur.com/WAq1drc.png)
### 建立單篇日記頁的 Template
回顧一下前面寫的 view function（post_detail）內容
```
return render(request, 'post.html', {'post': post})
```
我們取得所需 post 物件後，傳入 post.html 這個 template 中 render。現在我們就來完成這個 template。建立 post.html 如下：
```
<!-- templates/post.html -->

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>{{ post.title }} | A Django Ncku’s Adventure</title>
    <link href="//fonts.googleapis.com/css?family=Lemon" rel="stylesheet" type="text/css">
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">
    <link href="//djangogirlstaipei.github.io/assets/css/style.css" rel="stylesheet" type="text/css">
</head>
<body>
    <div class="header">
        <h1 class="site-title text-center">
            <a href="/">A Django Ncku’s Adventure</a>
        </h1>
    </div>
    <div class="container post post-detail">
        <div class="post-heading">
            <h1 class="title">{{ post.title }}</h1>
            <div class="date">{{ post.created_at|date:'Y / m / d' }}</div>
        </div>
        <div class="location">
            <i class="fa fa-map-marker"></i>
            <span id="location-content">{{ post.location }}</span>
        </div>
        <div id="map-canvas" class="map"></div>
        <div class="post-content">
            {{ post.content }}
        </div>
        <hr class="fancy-line">
        <img class="photo" src="{{ post.photo }}" alt="Cover photo for {{ post.title }}">
    </div>
    <script src="//maps.googleapis.com/maps/api/js?v=3.exp&libraries=places&sensor=false"></script>
    <script src="//djangogirlstaipei.github.io/assets/js/map.js"></script>
</body>
</html>
```
這個 template 將 post 物件的屬性 (e.g. 標題、內文、時間......等)，利用 {{ var }} 與 template filter 顯示並格式化於 HTML 中。若資料庫裡有 pk=3 的 Post，現在連至 http://127.0.0.1:8000/post/3/ 即可看到此日記的單頁。

---
#### {% url %}
連結到特定 view 的 template tag
使用方法：
![](https://i.imgur.com/cmlSLao.png)
也可以傳入參數，如：
```
{% url '<url_name>' arg1=<var1> arg2=<var2> ...%}
```
### 加入到單篇日記頁的連結
最後，我們還需在首頁加上單篇日記的連結。我們可以使用 {% url %} 這個 template tag 達成。需要加入的地方有：
1. 每篇日記
1. 每篇日記的 Read More 按鈕

#### 設定標題連結
打開 home.html，找到下面的內容：
```
<!-- home.html -->

<h2 class="title">
    <a href="#">{{ post.title }}</a>
</h2>
```
將它改成
```
<!-- home.html -->

<h2 class="title">
    <a href="{% url 'post_detail' pk=post.pk %}">{{ post.title }}</a>
</h2>
```
#### 設定 Read More 按鈕的連結
在 home.html 中找到以下內容：
```
<!-- home.html -->

<a class="read-more" href="#">
    Read More <i class="fa fa-arrow-right"></i>
</a>
```
修改如下：
```
<!-- home.html -->

<a class="read-more" href="{% url 'post_detail' pk=post.pk %}">
    Read More <i class="fa fa-arrow-right"></i>
</a>
```
### 驗收成果

