lenny5201314
  • NEW!
    NEW!  Connect Ideas Across Notes
    Save time and share insights. With Paragraph Citation, you can quote others’ work with source info built in. If someone cites your note, you’ll see a card showing where it’s used—bringing notes closer together.
    Got it
      • Create new note
      • Create a note from template
        • Sharing URL Link copied
        • /edit
        • View mode
          • Edit mode
          • View mode
          • Book mode
          • Slide mode
          Edit mode View mode Book mode Slide mode
        • Customize slides
        • Note Permission
        • Read
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Write
          • Only me
          • Signed-in users
          • Everyone
          Only me Signed-in users Everyone
        • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invite by email
        Invitee

        This note has no invitees

      • Publish Note

        Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

        Your note will be visible on your profile and discoverable by anyone.
        Your note is now live.
        This note is visible on your profile and discoverable online.
        Everyone on the web can find and read all notes of this public team.

        Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

        Explore these features while you wait
        Complete general settings
        Bookmark and like published notes
        Write a few more notes
        Complete general settings
        Write a few more notes
        See published notes
        Unpublish note
        Please check the box to agree to the Community Guidelines.
        View profile
      • Commenting
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
        • Everyone
      • Suggest edit
        Permission
        Disabled Forbidden Owners Signed-in users Everyone
      • Enable
      • Permission
        • Forbidden
        • Owners
        • Signed-in users
      • Emoji Reply
      • Enable
      • Versions and GitHub Sync
      • Note settings
      • Note Insights New
      • Engagement control
      • Make a copy
      • Transfer ownership
      • Delete this note
      • Save as template
      • Insert from template
      • Import from
        • Dropbox
        • Google Drive
        • Gist
        • Clipboard
      • Export to
        • Dropbox
        • Google Drive
        • Gist
      • Download
        • Markdown
        • HTML
        • Raw HTML
    Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
    Create Create new note Create a note from template
    Menu
    Options
    Engagement control Make a copy Transfer ownership Delete this note
    Import from
    Dropbox Google Drive Gist Clipboard
    Export to
    Dropbox Google Drive Gist
    Download
    Markdown HTML Raw HTML
    Back
    Sharing URL Link copied
    /edit
    View mode
    • Edit mode
    • View mode
    • Book mode
    • Slide mode
    Edit mode View mode Book mode Slide mode
    Customize slides
    Note Permission
    Read
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Write
    Only me
    • Only me
    • Signed-in users
    • Everyone
    Only me Signed-in users Everyone
    Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    --- 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> ``` ### 驗收成果

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully