實戰:記帳
接續上一個 assistant
專案與 journal
應用程式。現在想在數位助理專案額外加上記帳的功能,這功能跟原本的日誌是可以相互獨立運作。
在 Django 專案中,其實可以包含多個應用程式,接下來的記帳功能,就採用在同一個專案下新增另一個應用程式的方式來示範。
在專案中新增記帳應用程式
新增應用程式
先切換到專案資料夾下,再以管理腳本 manage.py
的 startapp
命令來新增應用程式:
python manage.py startapp expenses
expenses
是欲新增的應用程式名稱
將應用程式加入專案
新增完應用程式之後,還得修改專案的設定檔,將應用程式列入 INSTALLED_APPS
列表,才算將應用程式加入專案。
修改 assistant/assistant/setting.py
,新增第 41 行,在 INSTALLED_APPS
列表中加入方才新增的 expenses
應用程式:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'journal',
'expenses',
]
將應用程式定義的路徑規則加入專案
雖然目前還尚未定義記帳應用程式的路徑規則,不過要將專案加入應用程式的話,這個步驟也不能省。
開啟專案的路徑規則定義 assistant/assistant/urls.py
,新增第 25 行,將 expenses
應用程式中定義的路徑規則加入專案的路徑規則清單:
urlpatterns = [
path('admin/', admin.site.urls),
path('', RedirectView.as_view(url='journal/')),
path('journal/', include('journal.urls')),
path('accounts/', include('django.contrib.auth.urls')),
path('expenses/', include('expenses.urls')),
]
資料庫
定義資料模型
開啟 assistant/expense/models.py
,新增以下程式碼:
class Expense(models.Model):
CATE_CHOICES = (
(0, "未分類"),
(1, "飲食"),
(2, "衣服"),
(3, "交通"),
(4, "教育"),
(5, "娛樂"),
(99, "其它"),
)
item = models.CharField('項目', max_length=30)
category = models.IntegerField('支出類別', default=0, choices=CATE_CHOICES)
amount = models.IntegerField('支出金額', default=0)
time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.item
- 第 7-15 行,定義支出類別選項的值與其對應的標籤文字,
CATE_CHOICES
可自行命名。
- 每組選項值與標籤文字的對應關係以 值組(tuple) 的形式呈現
- 所有選項對應關係的列表可被放在 列表(list) 或 值組(tuple)
- 第 17-20 行,定義支出紀錄所需要的欄位
未指定 choices
屬性時,預設以單行文字框供使用者自行輸入
指定 choices
屬性時,預設會以下拉選單呈現選項
- 第 22-23 行,定義
__str__()
方法來回傳代表紀錄的字串
套用到資料庫
對資料模型進行新增、刪除、修改之後,還需要回到命令提示字元的視窗下輸入兩個指令,才能套用這些變更:
python manage.py makemigrations
python manage.py migrate
網址、視圖、範本、表單
定義路徑規則(urls.py
)
在這個例子中,我們會實作支出紀錄列表、新增支出紀錄、修改支出紀錄以及刪除支出紀錄等 4 個功能,首先來定義這個應用程式要處理的路徑規則。
新增應用程式的路徑規則定義檔 assistant/expenses/urls.py
,並在檔案內填入以下程式碼:
from django.urls import path
from .views import *
urlpatterns = [
path('', ExpenseList.as_view(), name='expense_list'),
path('create/', ExpenseCreate.as_view(), name='expense_create'),
path('<int:pk>/update/', ExpenseUpdate.as_view(), name='expense_update'),
path('<int:pk>/delete/', ExpenseDelete.as_view(), name='expense_delete'),
]
定義視圖(views.py
)
開啟 expenses
應用程式的視圖檔 assistant/expenses/views.py
,填入以下程式碼:
from django.views.generic import *
from django.contrib.auth.mixins import LoginRequiredMixin
from django.urls import reverse
from .models import Expense
class ExpenseList(LoginRequiredMixin, ListView):
model = Expense
ordering = ['-id']
paginate_by = 10
class ExpenseCreate(LoginRequiredMixin, CreateView):
model = Expense
fields = '__all__'
template_name = 'form.html'
def get_success_url(self):
return reverse('expense_list')
class ExpenseUpdate(LoginRequiredMixin, UpdateView):
model = Expense
fields = '__all__'
template_name = 'form.html'
def get_success_url(self):
return reverse('expense_list')
class ExpenseDelete(LoginRequiredMixin, DeleteView):
model = Expense
template_name = 'confirm_delete.html'
def get_success_url(self):
return reverse('expense_list')
支出紀錄的列表、新增、修改、刪除等 4 個功能,同樣沿用通用視圖類別來實作,另外因為也限定這 4 個功能都需要登入後才能操作,因此在定義視圖類別時,也都使用了 LoginRequiredMixin
這個混成類別來加入是否已經登入的權限檢查。
這邊刻意用比較麻煩的方式,在定義路徑時為規則命名,在通用視圖內定義 get_success_url()
到底有什麼用?想像一下如果日後因為某種原因,需要修改支出紀錄相關功能的存取路徑。如果使用現在示範的這種做法的話,只需要在 urls.py
修改路徑即可,不需要修改 views.py
,甚至頁面範本也不需要修改;反之,若採用先前的做法,直接指定 success_url
屬性,當路徑規則相對應的路徑變更的時候,views.py
裡的 success_url
對應到的目標也要跟著做修正,當然,頁面範本內使用到相關路徑的地方也需要修改。
定義頁面範本(templates)
支出紀錄的新增、修改、刪除,直接共用前一個日誌應用程式 journal
已定義的表單範本(form.html
)以及確認刪除範本(confirm_delete.html
)就好,只需要新增支出紀錄列表的頁面範本。
新增支出列表頁面範本
新增 assistant/templates/expenses
資料夾,並於該資料夾下新增 expense_list.html
。
開啟頁面範本檔案 assistant/templates/expenses/expense_list.html
,並填入以下程式碼:
{% extends "base.html" %}
{% block content %}
<h2>我的支出紀錄</h2>
<table>
<tr>
<th>時間</th>
<th>項目</th>
<th>類別</th>
<th>金額</th>
<th>功能</th>
</tr>
{% for expense in expense_list %}
<tr>
<td>({{ expense.time|date:"l" }}) {{ expense.time }}</td>
<td><a href="{% url 'expense_update' expense.id %}">{{ expense.item }}</a></td>
<td>{{ expense.get_category_display }}</td>
<td>{{ expense.amount }}</td>
<td><a href="{% url 'expense_delete' expense.id %}">刪除</a></td>
</tr>
{% endfor %}
</table>
{% include 'pagination.html' %}
{% endblock content %}
- 第 13-21 行,以迴圈標籤將取得的支出紀錄一筆一筆列出
- 第 16, 19 行,在指定修改、刪除支出紀錄的連結目標網址時採用官方建議的作法,以頁面範本的
{% url %}
標籤指定要使用哪條路徑規則來反向推導其對應的路徑。
- 第 17 行,印出支出紀錄的
category
欄位值對應的標籤文字
- 第 23 行,引用先前已經做好的分頁顯示的頁面範本來產生分頁連結
修改網站頁面基底範本
因為新增了支出紀錄的相關功能,所以需要修改網站基底範本,將「支出列表」以及「新增消費」連結加上,以方便使用相關功能。
開啟檔案 assistant/templates/base.html
,新增第 14, 15 行,新增「支出列表」及「新增消費」兩個連結:
<!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>
<a href="{% url 'expense_list' %}">支出列表</a>
<a href="{% url 'expense_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>
這裡使用了 Django 頁面範本的內建標籤 url
來傳回指定路徑規則的存取路徑,使用方式如下:
{% url '路徑規則名稱' %}
這邊可使用的 路徑規則名稱
必須是在 urls.py
裡定義路徑規則時,有額外透過 name
屬性命名過的為路徑規則。
關於頁面範本中內建的 {% url %}
標籤
如果有額外參數的話,可以將參數依序列在路徑規則名稱之後,多個參數之間以空格隔開,例:
{% url '路徑規則名稱' 參數1 參數2 參數3 %}
例如:範例中有一條路徑規則如下:
path('<int:pk>/update/', ExpenseUpdate.as_view(), name='expense_update'),
在頁面範本中的 expense
變數裡存放了某一筆支出紀錄,可以這樣產生修改該筆紀錄的路徑:
{% url 'expense_update' expense.id %}
實戰:記帳
接續上一個
assistant
專案與journal
應用程式。現在想在數位助理專案額外加上記帳的功能,這功能跟原本的日誌是可以相互獨立運作。在 Django 專案中,其實可以包含多個應用程式,接下來的記帳功能,就採用在同一個專案下新增另一個應用程式的方式來示範。
在專案中新增記帳應用程式
新增應用程式
先切換到專案資料夾下,再以管理腳本
manage.py
的startapp
命令來新增應用程式:expenses
是欲新增的應用程式名稱將應用程式加入專案
新增完應用程式之後,還得修改專案的設定檔,將應用程式列入
INSTALLED_APPS
列表,才算將應用程式加入專案。修改
assistant/assistant/setting.py
,新增第 41 行,在INSTALLED_APPS
列表中加入方才新增的expenses
應用程式:將應用程式定義的路徑規則加入專案
雖然目前還尚未定義記帳應用程式的路徑規則,不過要將專案加入應用程式的話,這個步驟也不能省。
開啟專案的路徑規則定義
assistant/assistant/urls.py
,新增第 25 行,將expenses
應用程式中定義的路徑規則加入專案的路徑規則清單:資料庫
定義資料模型
開啟
assistant/expense/models.py
,新增以下程式碼:CATE_CHOICES
可自行命名。未指定
choices
屬性時,預設以單行文字框供使用者自行輸入指定
choices
屬性時,預設會以下拉選單呈現選項__str__()
方法來回傳代表紀錄的字串套用到資料庫
對資料模型進行新增、刪除、修改之後,還需要回到命令提示字元的視窗下輸入兩個指令,才能套用這些變更:
網址、視圖、範本、表單
定義路徑規則(
urls.py
)在這個例子中,我們會實作支出紀錄列表、新增支出紀錄、修改支出紀錄以及刪除支出紀錄等 4 個功能,首先來定義這個應用程式要處理的路徑規則。
新增應用程式的路徑規則定義檔
assistant/expenses/urls.py
,並在檔案內填入以下程式碼:定義視圖(
views.py
)開啟
expenses
應用程式的視圖檔assistant/expenses/views.py
,填入以下程式碼:支出紀錄的列表、新增、修改、刪除等 4 個功能,同樣沿用通用視圖類別來實作,另外因為也限定這 4 個功能都需要登入後才能操作,因此在定義視圖類別時,也都使用了
LoginRequiredMixin
這個混成類別來加入是否已經登入的權限檢查。這邊刻意用比較麻煩的方式,在定義路徑時為規則命名,在通用視圖內定義
get_success_url()
到底有什麼用?想像一下如果日後因為某種原因,需要修改支出紀錄相關功能的存取路徑。如果使用現在示範的這種做法的話,只需要在urls.py
修改路徑即可,不需要修改views.py
,甚至頁面範本也不需要修改;反之,若採用先前的做法,直接指定success_url
屬性,當路徑規則相對應的路徑變更的時候,views.py
裡的success_url
對應到的目標也要跟著做修正,當然,頁面範本內使用到相關路徑的地方也需要修改。定義頁面範本(templates)
支出紀錄的新增、修改、刪除,直接共用前一個日誌應用程式
journal
已定義的表單範本(form.html
)以及確認刪除範本(confirm_delete.html
)就好,只需要新增支出紀錄列表的頁面範本。新增支出列表頁面範本
新增
assistant/templates/expenses
資料夾,並於該資料夾下新增expense_list.html
。開啟頁面範本檔案
assistant/templates/expenses/expense_list.html
,並填入以下程式碼:{% url %}
標籤指定要使用哪條路徑規則來反向推導其對應的路徑。category
欄位值對應的標籤文字修改網站頁面基底範本
因為新增了支出紀錄的相關功能,所以需要修改網站基底範本,將「支出列表」以及「新增消費」連結加上,以方便使用相關功能。
開啟檔案
assistant/templates/base.html
,新增第 14, 15 行,新增「支出列表」及「新增消費」兩個連結:這裡使用了 Django 頁面範本的內建標籤
url
來傳回指定路徑規則的存取路徑,使用方式如下:{% url '路徑規則名稱' %}
這邊可使用的
路徑規則名稱
必須是在urls.py
裡定義路徑規則時,有額外透過name
屬性命名過的為路徑規則。關於頁面範本中內建的
{% url %}
標籤如果有額外參數的話,可以將參數依序列在路徑規則名稱之後,多個參數之間以空格隔開,例:
{% url '路徑規則名稱' 參數1 參數2 參數3 %}
例如:範例中有一條路徑規則如下:
path('<int:pk>/update/', ExpenseUpdate.as_view(), name='expense_update'),
在頁面範本中的
expense
變數裡存放了某一筆支出紀錄,可以這樣產生修改該筆紀錄的路徑:{% url 'expense_update' expense.id %}