案例:留言板
前言
第 7 課已經大致說明了留言板的程式架構,接著來進行版面美化的工作。在這個案例中,同樣直接透過 CDN 來引用 Bootstrap 框架,同時為了讓頁面看起來更活潑,也會引用 Font Awesome 在頁面加上一些圖示。
修正範本路徑
在原本的實作中,頁面範本被分散在 guestbook/templates/
以及 guestbook/web/templates/
,為了方便管理,請將 guestbook/web/templates/
裡所有的檔案及資料夾移入 guestbook/templates/
,將這兩個資料夾合併在一起,完成後專案目錄的結構大致如下:
guestbook/
├── db.sqlite3
├── guestbook/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py*
├── templates/
│ ├── base.html
│ ├── confirm_delete.html
│ ├── form.html
│ ├── navbar.html
│ ├── registration/
│ │ ├── logged_out.html
│ │ └── login.html
│ └── web/
│ ├── message_detail.html
│ └── message_list.html
└── web/
├── __init__.py
├── admin.py
├── apps.py
├── models.py
├── tests.py
├── urls.py
└── views.py
引用 Bootstrap 與 FontAwesome 框架
如同前一個案例,先修改 guestbook/templates/base.html
:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">
<title>留言板</title>
</head>
<body>
<div class="container">
{% block content %}{% endblock %}
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
</body>
</html>
原本的「首頁」、「新增留言」、「登入」/「登出」等連結暫且移除,稍候製作導覽列時再將其移入。
加入頁面導覽列
新增 guestbook/templates/navbar.html
檔案,並鍵入以下內容:
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
<div class="navbar-brand">留言板</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
<li class="nav-item"><a href="/" class="nav-link">首頁</a></li>
<li class="nav-item"><a href="/message/create" class="nav-link">新增留言</a></li>
{% if user.is_authenticated %}
<li class="nav-item"><a href="/accounts/logout" class="nav-link btn btn-sm btn-primary">{{ user.username }} 登出</a></li>
{% else %}
<li class="nav-item"><a href="{% url 'login' %}" class="nav-link btn btn-sm btn-primary">登入</a></li>
{% endif %}
</ul>
</div>
</nav>
然後記得把這個範本加入 guestbook/templates/base.html
,這樣才會在每個頁面顯示。增加第 15 行的 {% include "navbar.html" %}
:
<div class="container">
{% include "navbar.html" %}
{% block content %}{% endblock %}
</div>
加入 Font Awesome 圖示
先到 Font Awesome 的圖示展示頁面(https://fontawesome.com/icons)搜尋需要的圖示,可以在頁面上的搜尋框輸入關鍵字來篩選圖示。列表中以深色呈現的圖示是可以免費使用的,淺色的圖示則是需要另外付費購買。
假設現在要在「留言板」前面加上一個代表「訊息」的圖示,可以在搜尋框輸入「message」來篩選圖示。
若想使用 comment-alt
這個圖示,可點選它查看細節。
重點在頁面左下方,有列出使用這個圖示的 HTML 碼 <i class="far fa-comment-alt"></i>
,把這段 HTML 碼放在導覽列的「留言板」之前,修改 guestbook/templates/navbar.html
的第 3 行:
<div class="navbar-brand"><i class="far fa-comment-alt"></i>留言板</div>
修改後的導覽列會變成這樣:
持續修改 guestbook/templates/navbar.html
,為「首頁」、「新增留言」、「登入」、「登出」等加上合適的圖示:
<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
<div class="navbar-brand"><i class="far fa-comment-alt"></i> 留言板</div>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ml-auto">
<li class="nav-item"><a href="/" class="nav-link"><i class="fas fa-home"></i> 首頁</a></li>
<li class="nav-item"><a href="/message/create" class="nav-link"><i class="fas fa-edit"></i> 新增留言</a></li>
{% if user.is_authenticated %}
<li class="nav-item"><a href="/accounts/logout" class="nav-link btn btn-sm btn-primary">{{ user.username }} <i class="fas fa-sign-out-alt"></i> 登出</a></li>
{% else %}
<li class="nav-item"><a href="{% url 'login' %}" class="nav-link btn btn-sm btn-primary"><i class="fas fa-sign-in-alt"></i> 登入</a></li>
{% endif %}
</ul>
</div>
</nav>
整個修改完的導覽列外觀如下,與原本純文字的導覽列相比,看起來稍微活潑了些:
修改頁面路徑規則
修改 guestbook/web/urls.py
:
from django.urls import path
from . import views
urlpatterns = [
path('', views.MessageListView.as_view()),
path('create/', views.MessageCreate.as_view()),
path('<int:pk>', views.MessageDetailView.as_view()),
path('<int:pk>/delete/', views.MessageDelete.as_view()),
]
另外也需要修改 guestbook/guestbook/urls.py
:
from django.contrib import admin
from django.urls import path
from django.conf.urls import include
from django.views.generic import RedirectView
urlpatterns = [
path('admin/', admin.site.urls),
path('', RedirectView.as_view(url='/message/')),
path('message/', include('web.urls')),
path('accounts/', include('django.contrib.auth.urls')),
]
主要修改第 8 行將首頁導向至留言列表頁面以及與第 9 行修正 web 模組的路徑。
美化其他頁面範本
留言列表
修改 guestbook/templates/web/message_list.html
,使用列表、清單(List Group)來顯示留言列表:
{% extends "base.html" %}
{% block content %}
<ul class="list-group mt-2">
{% for message in message_list %}
<li class="list-group-item list-group-item-action">
<small class="text-muted"><i class="fas fa-clock"></i> {{message.publication_date|date:"Y/m/d H:i"}}</small>
{% if user.is_authenticated %}
<a href="{{message.id}}/delete" class="text-danger" title="刪除"><i class="fas fa-minus-circle"></i></a>
{% endif %}
<a href="{{message.id}}">{{ message.subject }}</a>
</li>
{% endfor %}
</ul>
{% endblock %}
留言檢視
修改 guestbook/templates/web/message_detail.html
,同樣以資訊卡片(Card)來顯示留言的詳細內容:
{% extends "base.html" %}
{% block content %}
<div class="card mt-2">
<div class="card-header">
<div class="font-weight-bold"><i class="fas fa-file-alt"></i> {{ message.subject }}</div>
</div>
<div class="card-body">
<div class="card-text">{{ message.content|linebreaks }}</div>
</div>
<div class="card-footer d-flex justify-content-between">
<small class="text-muted"><i class="fas fa-user"></i> {{ message.user }}</small>
<small class="text-muted"><i class="fas fa-clock"></i> {{ message.publication_date|date:"Y/m/d H:i" }}</small>
</div>
</div>
{% endblock %}
新增留言
修改 guestbook/templates/form.html
,讓頁面上的表格也套用 Bootstrap 框架的表格外觀:
{% extends "base.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<table class="table table-sm">
{{ form.as_table }}
</table>
<input type="submit" value="送出" class="btn btn-sm btn-primary"/>
</form>
{% endblock %}
修改的地方只有第 6 行,為 <table>
標籤加上 class="table table-sm"
屬性,額外套用 table-sm
的原因是想讓表格的列可以排列得稍微緊緻一些。
接下來再來修正一下頁面上輸入元件的外觀。Bootstrap 框架也有輸入元件的相關定義,最基本的方式就是為這些輸入元件套用 form-control
類別,例:
<form>
<input type="text" class="form-control">
<textarea class="form-control"></textarea>
</form>
新增留言這個頁面範本產生輸入元件的方式是透過第 8 行的 {{ form.as_table }}
將接收到的 form
變數的內容轉成相對應的 HTML 碼,沒有辦法直接以新增屬性的方式來套用 CSS 類別。
在不安裝額外的 python 套件的前提下,有 2 種方式來解決這個問題:
- 在視圖(View)產生 form 的時候,就指定每個輸入元件要套用的 CSS 類別
- 不使用
{{ form.as_table }}
來自動產生 HTML 碼,而是自行撰寫這些輸入元件的 HTML 碼
方案1:預先指定要套用的 CSS 類別
看一下 guestbook/web/views.py
,新增留言是由 MessageCreate
這個 class 負責的,它繼承了 django 內建的 CreateView
類別,由我們指定的 Message
這個 model 自動生成 form 的定義。
class MessageCreate(CreateView):
model = Message
fields = ['user', 'subject', 'content']
success_url = "/"
template_name = 'form.html'
在這邊無法調整 form 的內容,所幸 CreateView 除了指定 model
與 fields
的方式自動生成 form 之外,也接受使用者以指定 form_class
的方式,轉而參考該 class 來產生 form。
新增 guestbook/web/forms.py
自行定義 form class:
from django import forms
from .models import Message
class MessageForm(forms.ModelForm):
class Meta:
model = Message
fields = ['user', 'subject', 'content']
widgets = {
'user': forms.TextInput(attrs={'class': 'form-control'}),
'subject': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control'}),
}
在這邊透過繼承 forms.ModelForm
的方式來定義 MessageForm
。定義好表單之後,再修正 guestbook/web/views.py
,修改 MessageCreate
的內容:
from .forms import MessageForm
class MessageCreate(CreateView):
form_class = MessageForm
success_url = "/"
template_name = 'form.html'
- 新增第 2 行來引用方才定義的
MessageForm
- 刪除原本
MessageCreate
裡的 model
以及 fields
,改以第 6 行的 form_class
來指定使用 MessageForm
方案2:自行撰寫 HTML 碼
採用這個方案的話,表示要完全忽略傳遞給頁面範本的 form
變數的內容,在範本中就不以 {{ form.as_table }}
來產生相關輸入元件的 HTML 碼。不需要新增 forms.py
,不需要修改 views.py
,僅修改頁面範本即可。
修改 guestbook/templates/form.html
:
{% extends "base.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<table class="table">
<tr>
<th><label for="id_user">姓名:</label></th>
<td><input id="id_user" name="user" class="form-control"></td>
</tr>
<tr>
<th><label for="id_subject">主旨:</label></th>
<td><input id="id_subject" name="subject" class="form-control"></td>
</tr>
<tr>
<th><label for="id_content">內容:</label></th>
<td><textarea name="content" id="id_content" cols="30" rows="10" class="form-control"></textarea></td>
</tr>
</table>
<input type="submit" value="送出" class="btn btn-sm btn-primary"/>
</form>
{% endblock %}
將原本的 {{ form.as_table }}
刪除,改以第 7-18 行的 HTML 碼取代。
採用這個方案要特別注意自行撰寫的 HTML 碼,每個輸入欄位的 name
屬性與視圖裡所參照的 model 是否有對應到,如果無法完全對應的話,可能有些欄位的值就無法被寫人資料庫。
建議盡量採用方案1,比較不會發生不慎打錯欄位名稱的狀況。
刪除留言
修改 guestbook/templates/confirm_delete.html
:
{% extends "base.html" %}
{% block content %}
<h1>刪除記錄</h1>
<p>您確定要刪除這筆記錄嗎?</p>
<form action="" method="POST">
{% csrf_token %}
<input type="submit" value="是的,我要刪除" class="btn btn-sm btn-danger" />
</form>
{% endblock %}
主要是第 8 行,為按鈕套用 btn btn-sm btn-danger
等類別。
使用者登入
修改 guestbook/templates/registration/login.html
:
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p class="alert alert-danger">帳號或密碼不符合,請再試一次。</p>
{% endif %}
<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<div class="h3 mb5">請輸入您的帳號密碼</div>
<div class="form-inline">
<input name="username" autofocus="" required="" id="id_username" maxlength="254" type="text" class="form-control mr-2" placeholder="帳號">
<input name="password" required="" id="id_password" type="password" class="form-control mr-2" placeholder="密碼">
<input type="submit" value="login" class="form-control btn-primary"/>
</div>
</form>
{% endblock %}
使用者登出
修改 guestbook/templates/registration/logged_out.html
:
{% extends "base.html" %}
{% block content %}
<p class="alert alert-info">您已登出!!</p>
<a href="{% url 'login'%}" class="btn btn-primary">請按此處重新<i class="fas fa-sign-in-alt"></i> 登入</a>
{% endblock %}
案例:留言板
前言
第 7 課已經大致說明了留言板的程式架構,接著來進行版面美化的工作。在這個案例中,同樣直接透過 CDN 來引用 Bootstrap 框架,同時為了讓頁面看起來更活潑,也會引用 Font Awesome[1] 在頁面加上一些圖示。
修正範本路徑
在原本的實作中,頁面範本被分散在
guestbook/templates/
以及guestbook/web/templates/
,為了方便管理,請將guestbook/web/templates/
裡所有的檔案及資料夾移入guestbook/templates/
,將這兩個資料夾合併在一起,完成後專案目錄的結構大致如下:引用 Bootstrap 與 FontAwesome 框架
如同前一個案例,先修改
guestbook/templates/base.html
:原本的「首頁」、「新增留言」、「登入」/「登出」等連結暫且移除,稍候製作導覽列時再將其移入。
加入頁面導覽列
新增
guestbook/templates/navbar.html
檔案,並鍵入以下內容:然後記得把這個範本加入
guestbook/templates/base.html
,這樣才會在每個頁面顯示。增加第 15 行的{% include "navbar.html" %}
:加入 Font Awesome 圖示
先到 Font Awesome 的圖示展示頁面(https://fontawesome.com/icons)搜尋需要的圖示,可以在頁面上的搜尋框輸入關鍵字來篩選圖示。列表中以深色呈現的圖示是可以免費使用的,淺色的圖示則是需要另外付費購買。
假設現在要在「留言板」前面加上一個代表「訊息」的圖示,可以在搜尋框輸入「message」來篩選圖示。
若想使用
comment-alt
這個圖示,可點選它查看細節。重點在頁面左下方,有列出使用這個圖示的 HTML 碼
<i class="far fa-comment-alt"></i>
,把這段 HTML 碼放在導覽列的「留言板」之前,修改guestbook/templates/navbar.html
的第 3 行:修改後的導覽列會變成這樣:
持續修改
guestbook/templates/navbar.html
,為「首頁」、「新增留言」、「登入」、「登出」等加上合適的圖示:整個修改完的導覽列外觀如下,與原本純文字的導覽列相比,看起來稍微活潑了些:
修改頁面路徑規則
修改
guestbook/web/urls.py
:另外也需要修改
guestbook/guestbook/urls.py
:主要修改第 8 行將首頁導向至留言列表頁面以及與第 9 行修正 web 模組的路徑。
美化其他頁面範本
留言列表
修改
guestbook/templates/web/message_list.html
,使用列表、清單(List Group)來顯示留言列表:留言檢視
修改
guestbook/templates/web/message_detail.html
,同樣以資訊卡片(Card)來顯示留言的詳細內容:新增留言
修改
guestbook/templates/form.html
,讓頁面上的表格也套用 Bootstrap 框架的表格外觀:修改的地方只有第 6 行,為
<table>
標籤加上class="table table-sm"
屬性,額外套用table-sm
的原因是想讓表格的列可以排列得稍微緊緻一些。接下來再來修正一下頁面上輸入元件的外觀。Bootstrap 框架也有輸入元件的相關定義,最基本的方式就是為這些輸入元件套用
form-control
類別,例:<form> <!-- ...略... --> <input type="text" class="form-control"> <!-- ...略... --> <textarea class="form-control"></textarea> <!-- ...略... --> </form>
新增留言這個頁面範本產生輸入元件的方式是透過第 8 行的
{{ form.as_table }}
將接收到的form
變數的內容轉成相對應的 HTML 碼,沒有辦法直接以新增屬性的方式來套用 CSS 類別。在不安裝額外的 python 套件的前提下,有 2 種方式來解決這個問題:
{{ form.as_table }}
來自動產生 HTML 碼,而是自行撰寫這些輸入元件的 HTML 碼方案1:預先指定要套用的 CSS 類別
看一下
guestbook/web/views.py
,新增留言是由MessageCreate
這個 class 負責的,它繼承了 django 內建的CreateView
類別,由我們指定的Message
這個 model 自動生成 form 的定義。在這邊無法調整 form 的內容,所幸 CreateView 除了指定
model
與fields
的方式自動生成 form 之外,也接受使用者以指定form_class
的方式,轉而參考該 class 來產生 form。新增
guestbook/web/forms.py
自行定義 form class:在這邊透過繼承
forms.ModelForm
的方式來定義MessageForm
。定義好表單之後,再修正guestbook/web/views.py
,修改MessageCreate
的內容:MessageForm
MessageCreate
裡的model
以及fields
,改以第 6 行的form_class
來指定使用MessageForm
方案2:自行撰寫 HTML 碼
採用這個方案的話,表示要完全忽略傳遞給頁面範本的
form
變數的內容,在範本中就不以{{ form.as_table }}
來產生相關輸入元件的 HTML 碼。不需要新增forms.py
,不需要修改views.py
,僅修改頁面範本即可。修改
guestbook/templates/form.html
:將原本的
{{ form.as_table }}
刪除,改以第 7-18 行的 HTML 碼取代。採用這個方案要特別注意自行撰寫的 HTML 碼,每個輸入欄位的
name
屬性與視圖裡所參照的 model 是否有對應到,如果無法完全對應的話,可能有些欄位的值就無法被寫人資料庫。建議盡量採用方案1,比較不會發生不慎打錯欄位名稱的狀況。
刪除留言
修改
guestbook/templates/confirm_delete.html
:主要是第 8 行,為按鈕套用
btn btn-sm btn-danger
等類別。使用者登入
修改
guestbook/templates/registration/login.html
:使用者登出
修改
guestbook/templates/registration/logged_out.html
:Font Awesome 也是一個 CSS 框架,比較特別的是它提供了一個風格統一的圖示庫,讓網頁設計師可以方便地在頁面上擺放圖示。
官網:https://fontawesome.com/ ↩︎