實戰:日誌
接下來我們要建立個人數位助理的網站專案,在這個專案中有 2 個獨立的功能:日誌以及記帳。
建立專案與應用程式
建立新專案
(1)在終端機中下達指令建立一個新的專案
django-admin.py startproject assistant
打完指令後,會產生一個 assistant
的資料夾
assistant/
manage.py
assistant/
__init__.py
settings.py
urls.py
wsgi.py
在專案下建立應用程式
cd assistant
python manage.py startapp web
修改 assistant/assistant/settings.py
:
ALLOWED_HOSTS = ['*']
INSTALLED_APPS = [
'web',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': ['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',
],
},
},
]
LANGUAGE_CODE = 'zh-hant'
TIME_ZONE = 'Asia/Taipei'
資料庫
修改 assistant/web/models.py
:
from django.db import models
class Journal(models.Model):
content = models.TextField("內容")
created = models.DateField(auto_now_add=True)
def __str__(self):
return self.content
執行以下指令建立資料庫:
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
開啟網站服務
python manage.py runserver 0:80
網址、視圖、範本、表單
網址
我們要建立相對應的網頁,開啟 assistant/assistant/urls.py
,修改為以下程式碼。
from django.contrib import admin
from django.urls import path, include
from django.views.generic import RedirectView
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('web.urls')),
]
新增檔案 assistant/web/urls.py
from django.urls import path
from django.views.generic import RedirectView
from .views import *
urlpatterns = [
path('', RedirectView.as_view(url='journal/')),
path('journal/', JournalList.as_view()),
path('journal/create/', JournalCreate.as_view()),
path('journal/<int:pk>/update/', JournalUpdate.as_view()),
path('journal/<int:pk>/delete/', JournalDelete.as_view()),
]
視圖
開啟 assistant/web/views.py
,修改為以下程式碼:
from django.shortcuts import render
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from .models import Journal
class JournalList(ListView):
model = Journal
ordering = ['-id']
class JournalCreate(CreateView):
model = Journal
fields = ['content']
success_url = '/journal/'
template_name = 'form.html'
class JournalUpdate(UpdateView):
model = Journal
fields = ['content']
success_url = '/journal/'
template_name = 'form.html'
class JournalDelete(DeleteView):
model = Journal
success_url = '/journal/'
template_name = 'confirm_delete.html'
範本
為了將所有的頁面範本集中管理,前面在專案設定檔中已經指定了頁面範本的搜尋路徑,接下來我們就將所有的頁面範本都放在這個資料夾下。
建立頁面範本資料夾
新增頁面範本資料夾 assistant/templates
建立 web
應用程式的頁面範本資料夾 assistant/templates/web
新增頁面範本
新增網站基底頁面範本 assistant/templates/base.html
:
<!DOCTYPE html>
<html lang="zh-hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>數位助理</title>
</head>
<body>
<h1>數位助理</h1>
<div>
<a href="/journal/">日誌列表</a>
<a href="/journal/create/">寫日誌</a>
</div>
<div>{% block content %}{% endblock %}</div>
</body>
</html>
新增日誌列表頁面範本 assistant/templates/web/journal_list.html
{% extends 'base.html' %}
{% block content %}
<h2>我的日誌:</h2>
<table>
<tr>
<th>時間</th>
<th>項目</th>
<th>操作</th>
</tr>
{% for journal in journal_list %}
<tr>
<td>{{ journal.created|date:"l" }}{{ journal.created }}</td>
<td><a href="{{ journal.id }}/update/">{{ journal.content }}</a></td>
<td><a href="{{ journal.id }}/delete/">刪除</a></td>
</tr>
{% endfor %}
</table>
{% endblock %}
新增共用編輯表單頁面範本 assistant/templates/form.html
{% extends 'base.html' %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="送出" />
</form>
{% endblock %}
新增共用刪除表單頁面範本 assistant/templates/confirm_delete.html
{% extends 'base.html' %}
{% block content %}
<h2>刪除紀錄</h2>
<p>{{ object }}</p>
<p>確定要刪除這筆記錄嗎?</p>
<form action="" method="post">
{% csrf_token %}
<input type="submit" value="是的,我要刪除" />
</form>
{% endblock %}
使用者登入與登出
新增登入登出路徑規則
開啟 assistant/assistant/urls.py
,新增以下程式碼:
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('web.urls')),
path('accounts/', include('django.contrib.auth.urls')),
]
開啟專案設定檔 assistant/assistant/settings.py
,增加以下程式碼:
登入登出頁面範本
新增資料夾 assistant/templates/registration
新增登入頁面範本 assistant/templates/registration/login.html
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p>帳號或密碼不符合,請再試一次。</p>
{% endif %}
<form action="" method="post">
{% csrf_token %}
<div>
<td>帳號:</td>
<td>{{ form.username }}</td>
</div>
<div>
<td>密碼:</td>
<td>{{ form.password }}</td>
</div>
<div>
<input type="submit" value="登入" />
</div>
</form>
{% endblock %}
新增登出頁面範本 assistant/templates/registration/logged_out.html
{% extends "base.html" %}
{% block content %}
<p>您已登出!!</p>
<a href="{% url 'login'%}">請按此處重新登入</a>
{% endblock %}
修改網站基底頁面範本 assistant/templates/base.htm
,加上登出/登入連結:
<!DOCTYPE html>
<html lang="zh-hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>數位助理</title>
</head>
<body>
<h1>數位助理</h1>
<div>
<a href="/journal/">日誌列表</a>
<a href="/journal/create/">寫日誌</a>
{% if user.is_authenticated %}
{{ user.username }} <a href="{% url 'logout' %}">登出</a>
{% else %}
<a href="{% url 'login' %}">登入</a>
{% endif %}
</div>
<div>{% block content %}{% endblock %}</div>
</body>
</html>
限制登入後才能執行日記功能
到目前為止,雖然提供了使用者登人與登出的功能,但實際上使用者登入與否並不影響他在這個網站上所能進行的操作,即使在沒登入的狀態也能寫日誌、修改日誌、甚至刪除日誌。
你可能會想到在可以頁面範本中檢查使用者的登入狀況,再決定是否要顯示相關的連結。但是,看不到連結,不代表不能手動在網址列打上這些功能的存取路徑,一旦使用者「猜」到他想執行的操作所對應到的路徑,他可以自己打在網址列上,再讓瀏覽器送出存取請求來執行他想進行的動作。
為了做好網站的權限管控,不能只把不讓使用者操作的連結藏起來就好,而是應該在處理使用者請求執行某項功能的時候,要同時檢核他的權限。
這個範例的需求比較簡單,因為它是個人用的數位助理,所以只要檢查使用者是否已登人就好,不需要更複雜的權限管理。
開啟 assistant/web/views.py
,修改為以下程式碼:
from django.shortcuts import render
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from .models import Journal
from django.contrib.auth.mixins import LoginRequiredMixin
class JournalList(LoginRequiredMixin, ListView):
model = Journal
ordering = ['-id']
class JournalCreate(LoginRequiredMixin, CreateView):
model = Journal
fields = ['content']
success_url = '/journal/'
template_name = 'form.html'
class JournalUpdate(LoginRequiredMixin, UpdateView):
model = Journal
fields = ['content']
success_url = '/journal/'
template_name = 'form.html'
class JournalDelete(LoginRequiredMixin, DeleteView):
model = Journal
success_url = '/journal/'
template_name = 'confirm_delete.html'
分頁顯示
在日誌列表的頁面上,會將所有的日誌記錄全部列出來,但當日誌經過一段時間的累積之後,在同一個頁面上要顯示所有的日誌,有可能會因為數量太大,而導致頁面產生的速度過慢。因此一般的網站在處理清單式的頁面的時候,通常都會採用分頁的手法,將所有紀錄拆分成多頁顯示,每一項最多僅顯示固定筆數的紀錄。
修改視圖,加入分頁設定
開啟 assistant/web/views.py
,修改 JournalList
類別:
class JournalList(LoginRequiredMixin, ListView):
model = Journal
ordering = ['-id']
paginate_by = 3
處理頁面範本,加入分頁連結
在視圖中指定 paginate_by
屬性值,ListView
會自動依據這個設定取出目前面頁的資料量。因此頁面範本中只能取得最多 paginated_by
筆數的紀錄,但在頁面上顯示這些紀錄後,我們目前的頁面範本並不會自動顯示上一頁、下一頁、或其他分頁頁碼的連結。這部份不是 ListView
的工作,頁面如何呈現,該呈現什麼,這是頁面範本的任務。因此需要修改頁面範本,來規範這些分頁的連結該出現在何處,以及如何呈現這些分頁連結。
建立分頁連結範本
首先新增分頁連結的範本 assistant/templates/pagination.html
:
{% if is_paginated %}
<div>
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">上一頁</a>
{% endif %}
{% for page in paginator.page_range %}
{% if page == page_obj.number %}
<a href="#"><font color="red">{{ page }}</font></a>
{% else %}
<a href="?page={{ page }}">{{ page }}</a>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">下一頁</a>
{% endif %}
</div>
{% endif %}
這個分頁連結的範本所產生的 HTML 碼並不是一個完整的頁面,僅產生分頁連結的部份而已。若有其他的頁面範本需要產生分頁連結的時候,可以在頁面範本中透過底下的標籤來引用分頁連結範本:
{% include 'pagination.html' %}
將分頁連結加入日誌列表
開啟日誌列表頁面範本 assistant/templates/web/journal_list.html
,修改為以下程式碼:
{% extends 'base.html' %}
{% block content %}
<h2>我的日誌</h2>
<table>
<tr>
<th>時間</th>
<th>項目</th>
<th>操作</th>
</tr>
{% for journal in journal_list %}
<tr>
<td>{{ journal.created|date:"l" }}{{ journal.created }}</td>
<td><a href="{{ journal.id }}/update/">{{ journal.content }}</a></td>
<td><a href="{{ journal.id }}/delete/">刪除</a></td>
</tr>
{% endfor %}
</table>
{% include 'pagination.html' %}
{% endblock %}
實戰:日誌
接下來我們要建立個人數位助理的網站專案,在這個專案中有 2 個獨立的功能:日誌以及記帳。
建立專案與應用程式
建立新專案
(1)在終端機中下達指令建立一個新的專案
打完指令後,會產生一個
assistant
的資料夾在專案下建立應用程式
修改
assistant/assistant/settings.py
:資料庫
修改
assistant/web/models.py
:執行以下指令建立資料庫:
開啟網站服務
網址、視圖、範本、表單
網址
我們要建立相對應的網頁,開啟
assistant/assistant/urls.py
,修改為以下程式碼。新增檔案
assistant/web/urls.py
視圖
開啟
assistant/web/views.py
,修改為以下程式碼:範本
為了將所有的頁面範本集中管理,前面在專案設定檔中已經指定了頁面範本的搜尋路徑,接下來我們就將所有的頁面範本都放在這個資料夾下。
建立頁面範本資料夾
新增頁面範本資料夾
assistant/templates
建立
web
應用程式的頁面範本資料夾assistant/templates/web
新增頁面範本
新增網站基底頁面範本
assistant/templates/base.html
:新增日誌列表頁面範本
assistant/templates/web/journal_list.html
新增共用編輯表單頁面範本
assistant/templates/form.html
新增共用刪除表單頁面範本
assistant/templates/confirm_delete.html
使用者登入與登出
新增登入登出路徑規則
開啟
assistant/assistant/urls.py
,新增以下程式碼:開啟專案設定檔
assistant/assistant/settings.py
,增加以下程式碼:登入登出頁面範本
新增資料夾
assistant/templates/registration
新增登入頁面範本
assistant/templates/registration/login.html
新增登出頁面範本
assistant/templates/registration/logged_out.html
修改網站基底頁面範本
assistant/templates/base.htm
,加上登出/登入連結:限制登入後才能執行日記功能
到目前為止,雖然提供了使用者登人與登出的功能,但實際上使用者登入與否並不影響他在這個網站上所能進行的操作,即使在沒登入的狀態也能寫日誌、修改日誌、甚至刪除日誌。
你可能會想到在可以頁面範本中檢查使用者的登入狀況,再決定是否要顯示相關的連結。但是,看不到連結,不代表不能手動在網址列打上這些功能的存取路徑,一旦使用者「猜」到他想執行的操作所對應到的路徑,他可以自己打在網址列上,再讓瀏覽器送出存取請求來執行他想進行的動作。
為了做好網站的權限管控,不能只把不讓使用者操作的連結藏起來就好,而是應該在處理使用者請求執行某項功能的時候,要同時檢核他的權限。
這個範例的需求比較簡單,因為它是個人用的數位助理,所以只要檢查使用者是否已登人就好,不需要更複雜的權限管理。
開啟
assistant/web/views.py
,修改為以下程式碼:分頁顯示
在日誌列表的頁面上,會將所有的日誌記錄全部列出來,但當日誌經過一段時間的累積之後,在同一個頁面上要顯示所有的日誌,有可能會因為數量太大,而導致頁面產生的速度過慢。因此一般的網站在處理清單式的頁面的時候,通常都會採用分頁的手法,將所有紀錄拆分成多頁顯示,每一項最多僅顯示固定筆數的紀錄。
修改視圖,加入分頁設定
開啟
assistant/web/views.py
,修改JournalList
類別:處理頁面範本,加入分頁連結
在視圖中指定
paginate_by
屬性值,ListView
會自動依據這個設定取出目前面頁的資料量。因此頁面範本中只能取得最多paginated_by
筆數的紀錄,但在頁面上顯示這些紀錄後,我們目前的頁面範本並不會自動顯示上一頁、下一頁、或其他分頁頁碼的連結。這部份不是ListView
的工作,頁面如何呈現,該呈現什麼,這是頁面範本的任務。因此需要修改頁面範本,來規範這些分頁的連結該出現在何處,以及如何呈現這些分頁連結。建立分頁連結範本
首先新增分頁連結的範本
assistant/templates/pagination.html
:這個分頁連結的範本所產生的 HTML 碼並不是一個完整的頁面,僅產生分頁連結的部份而已。若有其他的頁面範本需要產生分頁連結的時候,可以在頁面範本中透過底下的標籤來引用分頁連結範本:
{% include 'pagination.html' %}
將分頁連結加入日誌列表
開啟日誌列表頁面範本
assistant/templates/web/journal_list.html
,修改為以下程式碼: