owned this note
owned this note
Published
Linked with GitHub
---
title: Use Django to create a website
tags: Django, website
---
# **Django 網站開發**
> 【目次】
> [TOC]
---
>Reference Website:
>1. [Django documentation](https://docs.djangoproject.com/en/2.1/)
>2. [Django Packages(可直接引用"功能套件",不用自己重複造)](https://djangopackages.org/)
>3. [Stack Overflow (技術交流平台--寫程式遇到問題,用以尋求解答)](https://stackoverflow.com/)
>4. [github (專案的免費開源分享空間--專案下載、協作)](https://github.com/)
>5. [Model field reference](https://docs.djangoproject.com/en/2.1/ref/models/fields/)
>6. [Making queries](https://docs.djangoproject.com/en/2.1/topics/db/queries/)
>7. [Templates](https://docs.djangoproject.com/en/2.1/ref/templates/)
---
## **課程簡介與目標**
* ==Django 是 **python** 實作而成的**網頁框架**==
* 具備基本的 python 能力,並懂一點 HTML、CSS 與 Javascript
* 建立簡單的靜態網頁,並加入動態資料
* 將網頁應用程式佈署到免費雲端空間
---
## **網頁運作原理**
* 一個網頁在運作時,主要有三項參與其中:
1. ==client(瀏覽器)==
2. ==web server(資料的運算和處理)==
3. ==database(儲存資料)==
* 流程:
![](https://i.imgur.com/svkm2kX.png)
> `client` 瀏覽器輸入網址
> ----> 網址會被路由器帶到正確的 `web server` 的位置
> ----> web server 接收到請求後,視其必要跟 `database` 索取資料
>
> 索取 `database` 資料後
> ----> 經過 `web server` 對資料的運算和處理
> ----> 再把結果傳回 `client` 瀏覽器
---
## **Django 基本介紹**
* 誰使用 Django ?
[Top 10 sites built with Django Framework:Instagram, Mozilla Firefox, Pinterest, NASA ... ](https://www.linkedin.com/pulse/top-10-sites-built-django-framework-vladimir-bogdanov)
* ==Django 特點==
* 歷史悠久,經得起考驗。 (首次發行:2005年7月)
* 市佔率最高
* 最完整的技術文件
* 豐富的套件
* 強大的社群 (當開發上遇到困難,都能快速的找到解決方法)
* 提供 total solution,初學者可透過 Django 內建的模塊了解網站運作。(包山包海的套件,已內建 各種網站運作需要的模塊)
---
![](https://i.imgur.com/6sggp48.png)
| ==**知名の Python 網頁框架**== | 特點 | 備註 |
| ------- | ------------------------| ---------------------------------- |
| Django | 大而全,內建全套解決方案 | 讓初學者能專注搞懂如何運作,就能快速開發網站 |
| Flask | 微框架,輕盈,簡單 | 適用時機:小專案 |
| Tornado | 微框架,少而精,non-blocking network IO | 可實現異步操作 |
| Pyramid | 注重靈活性,所有模塊都需另外找套件搭配運用 | Django 的相反版本,適合"不喜歡被某個套件綁住的人使用" |
---
* Django 的原理與架構
![](https://i.imgur.com/5RhrIoK.png)
* ==Django 存放在 web server==
* MTV
* ==Models(定義資料庫的結構,e.g.XX資料表中,有XX欄位.欄位長度.是否可為空值...)==
* ==Templates(最後的 HTML 呈現)==
* ==Views(資料的邏輯運算)==
* Browser --> URLS --> Views --> Models ---(ORM 語法)---> Database
--> Models --> Views --> Templates --> Browser
* Why MTV ?
* 把 Models, Templates, Views 做切割
* 避免 Spaghetti Code (捲成一團)
1. 分離商業邏輯(後端)與UI(前端)
2. 前後端可獨立作業
3. 擁有更多彈性
4. 較容易維護
5. 降低複雜度
> ==傳統 ---> 把 Models, Templates, Views 寫在同一支程式中,造成混亂和維護困難==
---
:::info
### **Step00:搭建 python3 虛擬環境**
> * 把它想像成 --- ==完全獨立且隔絕的作業環境==
> 專門給這個專案使用の
> 完全不會跟原本的作業環境有衝突
> * why 虛擬環境?
> * 假設"現在"作業環境中的套件是"最新的版本",但此專案卻需要舊的版本。
> * ~~把新的版本降為舊的版本~~
==新建 Django 專案資料夾==
* myproject 代換為你所想命名的"資料夾名稱"
```
~$ mkdir myproject_test
~$ cd myproject_test
```
==在 Django 專案資料夾中,建立 python3 虛擬環境==
* venv 套件
* py3env 代換為你所想命名的"虛擬環境名稱"
* 啟動 python3 虛擬環境的資料夾
```
(linux/mac)
~/myproject_test$ python3 -m venv py3env
(windows)
C:\Python35\python -m venv myvenv
```
==啟動虛擬環境==
* 出現 (py3env) --> 成功
* 關閉虛擬環境:
* ~/myproject_test$ deactivate
* (py3env) 消失了~
```
(linux/mac)
~/myproject_test$ source py3env/bin/activate
(windows)
py3env/Scripts/activate
```
---
### **Step01:在 python3 虛擬環境下,安裝 Django**
==下載與安裝 Django==
* 若要安裝 Django 的某個特定版本
* ```pip install Django==1.9.10```
* ```pip install Django```
```
~/myproject_test$ pip install Django==1.9.10
```
* ```pip freeze``` 檢查當前的 Django 版本
```
~/myproject_test$ pip freeze
```
:::
---
# **在本關卡中會學習到什麼**
* Django 網站開發:快速打造完美網頁應用程式
* 自己打造專屬の==記帳 app==
---
# **Step1:建立 Django の project**
## **project 與 apps**
把一個 ==project 專案== 想像成 ==一個網站==
* 以 project 為最上層單位,==一個 project 可以包含多個 app==
* What is app?
* 代表 ==同一個網站中,不同的功能模塊==
* 如同 樂高積木,可隨時擴充、拔脫,也可把 app 拿去給別的 project 使用
* 使用時機:一個論壇類型的網站可能會有會員管理功能、語音內容區塊、文字內容區塊...等,可建立不同的 app 來管理。
```
django-admin startproject project_chia .
```
* project_chia 代換為 你所想命名的"資料夾名稱"
* . 代表 在當下的位置
![](https://i.imgur.com/Lcc5dRA.png)
```htmlembedded=
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ ls
py3env
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ django-admin startproject project_chia .
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ ls
manage.py project_chia py3env
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ cd project_chia
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/project_chia$ ls
__init__.py settings.py urls.py wsgi.py
```
>![](https://i.imgur.com/AzNnim2.png)
>
>**解釋各個 .py 的作用:**
>* manage.py:跟 django-admin 幾乎是一模一樣的功能,它是一個管理器。很多功能都需要透過這個管理器來執行。
>* project_chia 資料夾底下有 settings.py、urls.py、wsgi.py、init.py
> * settings.py:Django 中各種環境設定
> * urls.py:根據瀏覽器輸入的**網址**,指派 views.py 底下的哪一支程式來做事情
> * wsgi.py:web server 跟網頁應用程式(django)之間如何做溝通
> * init.py:讓 project_chia 變成一個套件模組
---
# **Step2:Project 參數設定介紹**
## 編輯 settings.py
* [Sublime Text 3 編輯器](https://www.sublimetext.com/3)
```python=
# 這些 app 是 Django 內建的 app ,
# 亦即"一個 project 可包含多個 app" の app
INSTALLED_APPS = [
'django.contrib.admin', #後台管理系統
'django.contrib.auth', #權限的管控
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles', #我們要建立static資料夾
]
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates', #DjangoTemplates-->我們要建立Templates資料夾
'DIRS': [], #定義templates的路徑,它是一個list
'APP_DIRS': True, #當我們以後在project底下建立app之後,Django會自動探索app底下是否有templates的資源
'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',
],
},
},
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', #sqlite3是Django裡預設的資料庫
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), #最後會產生db.sqlite3的檔案
}
}
TIME_ZONE = 'UTC' #時區:世界標準時間
* 改為--->TIME_ZONE = 'Asia/Taipei' #時區:台北
STATIC_URL = '/static/' #存取靜態資源(CSS, JavaScript, Images)
```
## 為 project 建立專屬資料庫
* 回到 manage.py 所在的位置,再執行 ```python manage.py migrate```
* 資料的寫進寫出,都是與 db.sqlite3 做互動
* 與資料庫結構相關的檔案,還有 models.py (之後會使用到~先記在腦袋裡)
![](https://i.imgur.com/5OtMCMF.png)
```htmlmixed=
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/project_chia$ ls
__init__.py settings.py urls.py wsgi.py
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/project_chia$ cd ..
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ ls
manage.py project_chia py3env
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ python manage.py migrate
Operations to perform:
Apply all migrations: contenttypes, auth, admin, sessions
Running migrations:
Rendering model states... DONE
Applying contenttypes.0001_initial... OK
...
Applying sessions.0001_initial... OK
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ ls
db.sqlite3 manage.py project_chia py3env
```
## 建立app
* 回到 manage.py 所在的位置,再執行 ```python manage.py startapp chia_app```
![](https://i.imgur.com/BFmUksF.png)
```htmlmixed=
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ python manage.py startapp chia_app
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ ls
chia_app db.sqlite3 manage.py project_chia py3env
```
>![](https://i.imgur.com/4pWdhga.png)
>
>**解釋各個 .py 的作用:**
>* chia_app 資料夾底下有 models.py、migrations (資料夾)、admin.py、views.py、tests.py、apps.py、init.py
> * models.py:
> * 把它當作一個空的 database
> * 設定 database 的結構:可建立一個一個的 table,每個 table 中又有好幾個 column
> * migrations (資料夾):每當我們對 models.py 做修改,紀錄修改 database 的歷史步驟,還有版本控制的功能
> * admin.py:後台管理介面的管理程式。
> * 打開後台管理介面針對 models.py 中所定義的 table 做其內容的 "新增/修改/刪除"
> * views.py:定義各種邏輯運算
> * tests.py:方便我們測試的檔案
> * apps.py:針對 app 的組態管理,存放這支 app 的 meta 檔案(目前比較不會用到)
---
# Step3:製作網頁的第一步:利用 Django 生成靜態網頁
## **在本關卡中會學習到什麼**
* 初步熟悉 Django 的 MTV 架構
* 使用 Django 生成簡單的靜態網頁
* 靜態網頁:
先不跟 database 做互動,只做單純的展示功能。資料寫死在 HTML 中。
---
## 建立一個簡單的靜態網頁
* 打開 Sublime Text 3 ,建立 example_hello.html
* 路徑:/home/testchia/myproject_test/example_hello.html
```htmlmixed=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Example_chia</title>
</head>
<body>
<h1>Hello Django 0.0</h1>
</body>
</html>
```
---
## Url Dispatcher - 如何解析使用者於瀏覽器輸入的 url
* Url Dispatcher:使用 urls.py
* urls.py 用來解析使用者於瀏覽器輸入的 url
* 根據解析後的結果,去指派某一支特定的程式來做運作
### ==urls.py==
```python=
from django.conf.urls import url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
#若輸入的網址符合 admin/ 的patterns,才會指派admin.site.urls這支程式來運作 => 開啟後台管理介面
#admin.site.urls這支程式 --> django內建之名稱為admin的apps其底下有一支程式叫site,site.py裡面有一個叫urls的function
]
```
:::danger
* Regular Expression:
* ```^```: 以什麼為開頭
* ```$```: 以什麼為結尾
* ```.```: 任意字元
* ```+```: 一個字元以上
* ```?```: 是否有某個字元出現都可以
* ```\d```: 數字
* ```()```: 擷取符合的 pattern
:::
* 回到 manage.py 所在的位置,再執行 ```python manage.py runserver```
```htmlmixed=
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ python manage.py runserver
Performing system checks...
System check identified no issues (0 silenced).
August 31, 2018 - 16:21:17
Django version 1.9.10, using settings 'project_chia.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Not Found: /
[31/Aug/2018 16:21:20] "GET / HTTP/1.1" 200 1767
Not Found: /favicon.ico
[31/Aug/2018 16:21:20] "GET /favicon.ico HTTP/1.1" 404 1942
```
* http://127.0.0.1:8000/
---> http://127.0.0.1 網頁伺服器現在搭建在"本機端localhost的電腦"
---> :8000/ 由於本機端裝有各式各樣的應用,這邊就必須指定其中一個入口(專門給 Django 使用)
* 若想更改原本預設的 port 為 8001,並 runserver
* ```python manage.py runserver 8001```
* ```127.0.0.1:8001/admin```
![](https://i.imgur.com/xDzYZPm.png)
---
## Url Dispatcher and View - Django 如何根據 url 指派正確的 view 程式運行
* Url Dispatcher:使用 views.py
### ==urls.py==
[方法一] 效能好
```python=
from django.conf.urls import url
from django.contrib import admin
from chia_app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^hello/', views.hello),
]
```
[方法二] 效能差一點,因為 Django 需要另外去解析
```python=
from django.conf.urls import url
from django.contrib import admin
#from chia_app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
#url(r'^hello/', views.hello),
url(r'^hello/', 'chia_app.views.hello'),
]
```
### ==views.py==
```python=
from django.shortcuts import render, HttpResponse
# Create your views here.
def hello(request):
return HttpResponse('HELLO 0.0')
```
![](https://i.imgur.com/IZl9p5Q.png)
## Views and Templates - 用 Django 生成靜態網頁
* 用 render 來達成 Views 和 Templates 的溝通
* 將之前寫的 example_hello.html 放入 templates 資料夾
* 路徑:/home/testchia/myproject_test/chia_app/templates/app/example_hello.html
* 改寫 views.py
* 在 settings.py 加入 chia_app
```python=
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ cd chia_app
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app$ mkdir templates
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app$ cd templates
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app/templates$ mkdir app
```
### ==views.py==
```python=
from django.shortcuts import render, HttpResponse
# Create your views here.
def hello(request):
return render(request,'app/example_hello.html',{})
```
### ==settings.py==
```python=
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'chia_app',
]
```
![](https://i.imgur.com/hHuOfID.png)
## static 資料夾 - 裝飾網頁
* [使用 bootstrap 的免費模板](https://getbootstrap.com/docs/4.1/examples/) ---> download "Dashboard" 並修改它
* bootstrap 是免費的 CSS, JavaScript 的資源套件,我們可以把它 download 並套用
![](https://i.imgur.com/xHBoYFH.png)
### ==擺放位置==
1. 找到 "Dashboard" 所在的位置:/home/testchia/下載/bootstrap-4.1.3/site/docs/4.1/examples/dashboard
2. 將 index.html 移至 /home/testchia/myproject_test/chia_app/templates/app/index.html
3. 將 dashboard.css 移至 /home/testchia/myproject_test/chia_app/static/css/dashboard.css
```htmlmixed=
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ cd chia_app
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app$ mkdir static
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app$ cd static
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app/static$ mkdir css
```
4. 放置 bootstrap.min.css 到路徑:/home/testchia/myproject_test/chia_app/static/css/bootstrap.min.css
### ==urls.py==
```python=
from django.conf.urls import url
from django.contrib import admin
from chia_app import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^hello/', views.hello),
url(r'^$', views.frontpage),
]
```
### ==views.py==
```python=
from django.shortcuts import render, HttpResponse
# Create your views here.
def hello(request):
return render(request,'app/example_hello.html',{})
def frontpage(request):
return render(request,'app/index.html',{})
```
### ==修改路徑 index.html==
[方法一] (寫死的方式)
```htmlmixed=
<link rel="icon" href="../../../../favicon.ico"> #刪除
<!-- Bootstrap core CSS -->
<link href="static/css/bootstrap.min.css" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="static/css/dashboard.css" rel="stylesheet">
```
[方法二] (有彈性的方式)
```htmlmixed=
{% load staticfiles %} #加上
<link rel="icon" href="../../../../favicon.ico"> #刪除
<!-- Bootstrap core CSS -->
<!-- Bootstrap core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="{% static 'css/dashboard.css' %}" rel="stylesheet">
```
![](https://i.imgur.com/sDQVxjP.png)
---
# Step4:利用 Django 製作動態網頁 --- 以記帳本網頁應用程式為例
## **在本關卡中會學習到什麼**
* 使用動態資料產生網頁內容
* 製作可互動的網頁
* 動態網頁:跟 database 做互動。
---
## Model - 操作資料庫結構
* 使用 Django 的 Model 定義資料庫結構
### ==models.py==
* 一個 ==類別(class)== 就是 ==1個table==
* 本專案需要 2 個 table:
* 紀錄(欄位包含:日期、描述、分類、金額、收支)
* 分類設定(欄位包含:分類自己本身 ---> 自己輸入的分類)
```python=
from django.db import models
from django.db.models import CharField,DateField,ForeignKey,IntegerField
# Create your models here.
BALANCE_TYPE = ((u'收入',u'收入'),(u'支出',u'支出')) #元組 tuple:可以視為不可改變的串列 ((key,value),(key,value))
class Category(models.Model):
category = CharField(max_length=20)
class Record(models.Model):
date = DateField()
description = CharField(max_length=300)
category = ForeignKey(Category,on_delete=models.SET_NULL,null=True) #外來鍵是一個(或數個)指向另外一個表格主鍵的欄位。
cash = IntegerField()
balance_type= CharField(max_length=2,choices=BALANCE_TYPE) #收/支choices只有兩種,已定義在上方的BALANCE_TYPE
```
* [元組 tuple:可以視為不可改變的串列](https://blog.kdchang.cc/2017/08/15/learning-programming-and-coding-with-python-list-tuple-dict-set/)
* 元組比起串列好處:
* 佔用空間較少
* 可以當做字典的 key(因不可變)
* 具名 tuple 可當做物件替代
* 當做函式引數
* [Python 的編碼](https://openhome.cc/Gossip/Encoding/Python.html)
:::danger
* 示範專案用到的 Model field types:
* ```models.CharField``` : varchar
* ```models.IntegerField``` : int
* ```models.DateField``` : date
* ```models.ForeignKey``` : foreign key
* ForeignKey 在資料庫中當作 Constraint 使用
* https://www.1keydata.com/tw/sql/sql-foreign-key.html
* http://www.w3school.com.cn/sql/sql_foreignkey.asp
* ForeignKey 的 on_delete 參數:
* models.CASCADE:相關的資料,全部都會被刪除
* models.PROTECT:不允許某個資料被刪除
* models.SET_NULL:要搭配 null = True。被刪除的,會被設成 None
* models.SET_DEFAULT:要搭配 default。被刪除的,會被設成預設的 未分類
* models.SET():要傳入 function
:::
## Model - 操作資料庫的版本控制 - migrations 資料夾
* 理解 migrations 的用法與意義
* 回到 manage.py 所在的位置,再執行
* 建立 migrations 腳本 (此操作 尚未更動資料庫)
```python manage.py makemigrations [app]```
> 出現 0001_initial.py 腳本檔案(版本): /home/testchia/myproject_test/chia_app/migrations/0001_initial.py
* 執行腳本 (此操作 更動資料庫)
```python manage.py migrate [chia_app]```
* 退回特定版本 (使用時機:當後悔更動資料庫時)
```python manage.py migrate [chia_app] [migrate version(0001_initial -->想要退回的版本名稱)]```
## 操作網頁 後台管理介面
* 使用後台管理介面可輕鬆管理資料庫內容
### ==建立 Super user 帳號、密碼==
* 回到 manage.py 所在的位置,再執行
```python manage.py createsuperuser```
* 統一建立
帳號:admin
密碼:@@12345678
```python manage.py runserver```
### ==admin.py==
* 要先到 admin.py 註冊 models.py 上面所寫的 2 個 table,這樣才能在後台管理介面中做內容的編輯
```python=
from django.contrib import admin
from .models import Record, Category
# Register your models here.
admin.site.register(Record)
admin.site.register(Category)
```
### 進入後台管理介面
![](https://i.imgur.com/mMR7vJv.png)
![](https://i.imgur.com/3wmRlFs.png)
![](https://i.imgur.com/fthjTkB.png)
### 在==models.py==中定義,將後台管理介面的英文顯示,變成中文
```python=
from django.db import models
from django.db.models import CharField,DateField,ForeignKey,IntegerField
# Create your models here.
BALANCE_TYPE = ((u'收入',u'收入'),(u'支出',u'支出')) #元組 tuple:可以視為不可改變的串列 ((key,value),(key,value))
class Category(models.Model):
category = CharField(max_length=20)
def __str__(self):
return self.category
class Record(models.Model):
date = DateField()
description = CharField(max_length=300)
category = ForeignKey(Category,on_delete=models.SET_NULL,null=True) #外來鍵是一個(或數個)指向另外一個表格主鍵的欄位。
cash = IntegerField()
balance_type= CharField(max_length=2,choices=BALANCE_TYPE) #收/支choices只有兩種,已定義在上方的BALANCE_TYPE
def __str__(self):
return self.description
```
![](https://i.imgur.com/xnBmb87.png)
![](https://i.imgur.com/5yW0uq9.png)
## Django ORM 語法 -- 操作資料庫中的資料
* [教學影片:6-5.Django ORM語法操作資料庫中的資料.mp4](https://drive.google.com/file/d/1E68XUj3PjQgXuu0cI0LJIMYlE-xnKKwW/view)
* 學習透過 Django ORM 與資料庫對話
![](https://i.imgur.com/zm7hODp.png)
![](https://i.imgur.com/1zSv4bm.png)
![](https://i.imgur.com/OrIGPlW.png)
![](https://i.imgur.com/VHzpUCZ.png)
```python=
>>> dir(r_get) #不能再做其他query的動作
>>> dir(r_filter) #可再做其他query的動作
>>> r_filter[0]
>>> r_filter[0] == r_get
```
![](https://i.imgur.com/uF9Pq9f.png)
![](https://i.imgur.com/z5CqYYn.png)
## MTV 協作 - 使用資料庫內的動態資料渲染網頁
* 理解 MTV 之間如何協調溝通,使用動態資料(資料庫中的資料)生成網頁內容。
### ==修改 index.html==
```htmlmixed=
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="{% static 'css/dashboard.css' %}" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark fixed-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="#">My Project</a>
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="/accounts/logout">Logout</a>
</li>
</ul>
</nav>
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="#">
<span data-feather="home"></span>
帳務總覽<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span data-feather="file"></span>
設定
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="page-header">帳務總覽</h1>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="row placeholders">
<div class="col-xs-8 col-sm-4 placeholder">
<h3>收入</h3>
<span class="text-muted"><h2>{{ income }}</h2></span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h3>支出</h3>
<span class="text-muted"><h2><font color="#DF565C">{{ outcome }}</font></h2></span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h3>存款</h3>
<span class="text-muted"><h2><font color="#53DF7D">{{ net }}</font></h2></span>
</div>
</div>
</div>
</main>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<h2 class="sub-header">歷史記錄</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>日期</th>
<th>描述</th>
<th>分類</th>
<th>金額</th>
<th>收/支</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</main>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
</body>
</html>
```
### ==urls.py==
* chia_app 底下的 urls.py(將 urls.py 複製並新增至 chia_app 底下),路徑:/home/testchia/myproject_test/chia_app/urls.py
```python=
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.frontpage),
]
```
* project_chia 底下的 urls.py,路徑:/home/testchia/myproject_test/project_chia/urls.py
```python=
"""project_chia URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.9/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include('chia_app.urls')),
]
```
### ==index.html==
* The Django template language
* Variables:```{{ variable }}```
* Tags:```{% tag %}``` 處理邏輯的部份
:::warning
> ```
> {{ records }}
> ```
> ![](https://i.imgur.com/Rl8W0D5.png)
> ![](https://i.imgur.com/aP5UoAZ.png)
:::
:::info
> ```
> {% for record in records %}
> <tr>
> <td>{{ record.date }}</td>
> <td>{{ record.description }}</td>
> <td>{{ record.category }}</td>
> <td>{{ record.cash }}</td>
> <td>{{ record.balance_type }}</td>
> </tr>
> {% endfor %}
> ```
> ![](https://i.imgur.com/j0JLxxB.png)
> ![](https://i.imgur.com/h55xEQW.png)
:::
:::success
> ```
> {% for record in records %}
> <tr>
> <td>{{ record.date | date:"Y-m-d"}}</td>
> <td>{{ record.description }}</td>
> <td>{{ record.category }}</td>
> <td>{{ record.cash }}</td>
> <td>{{ record.balance_type }}</td>
> </tr>
> {% endfor %}
> ```
> ![](https://i.imgur.com/p9NLLNb.png)
> ![](https://i.imgur.com/Hy0YmXy.png)
:::
### ==views.py==
```python=
from django.shortcuts import render, HttpResponse
from .models import Record
# Create your views here.
def hello(request):
return render(request,'app/example_hello.html',{})
def frontpage(request):
records = Record.objects.filter()
income_list = [record.cash for record in records if record.balance_type == '收入']
outcome_list = [record.cash for record in records if record.balance_type == '支出']
income = sum(income_list) if len(income_list)!=0 else 0
outcome = sum(outcome_list) if len(outcome_list)!=0 else 0
net = income - outcome
return render(request,'app/index.html',{'records':records,'income':income,'outcome':outcome,'net':net})
#可改寫成 return render(request,'app/index.html',locals())
```
![](https://i.imgur.com/3be1qTS.png)
# Step5:Template - 模版繼承避免重複的 HTML code
## ==base.html==
```htmlmixed=
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="{% static 'css/dashboard.css' %}" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark fixed-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="#">My Project</a>
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="/accounts/logout">Logout</a>
</li>
</ul>
</nav>
{% block content %} {% endblock %}
<!-- Bootstrap core JavaScript
================================================== -->
</body>
</html>
```
## ==index.html==
```htmlmixed=
{% extends 'app/base.html' %}
{% block content %}
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="/">
<span data-feather="home"></span>
帳務總覽<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/settings">
<span data-feather="file"></span>
設定
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="page-header">帳務總覽</h1>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="row placeholders">
<div class="col-xs-8 col-sm-4 placeholder">
<h3>收入</h3>
<span class="text-muted"><h2>{{ income }}</h2></span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h3>支出</h3>
<span class="text-muted"><h2><font color="#DF565C">{{ outcome }}</font></h2></span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h3>存款</h3>
<span class="text-muted"><h2><font color="#53DF7D">{{ net }}</font></h2></span>
</div>
</div>
</div>
</main>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<h2 class="sub-header">歷史記錄</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>日期</th>
<th>描述</th>
<th>分類</th>
<th>金額</th>
<th>收/支</th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr>
<td>{{ record.date | date:"Y-m-d"}}</td>
<td>{{ record.description }}</td>
<td>{{ record.category }}</td>
<td>{{ record.cash }}</td>
<td>{{ record.balance_type }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</main>
</div>
</div>
{% endblock %}
```
# Step6:製作 settings.html 設定的頁面
## chia_app 底下的 ==urls.py==
```python=
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.frontpage),
url(r'^settings$', views.settings),
]
```
## ==views.py==
```python=
from django.shortcuts import render, HttpResponse
from .models import Record
# Create your views here.
def hello(request):
return render(request,'app/example_hello.html',{})
def frontpage(request):
records = Record.objects.filter()
income_list = [record.cash for record in records if record.balance_type == '收入']
outcome_list = [record.cash for record in records if record.balance_type == '支出']
income = sum(income_list) if len(income_list)!=0 else 0
outcome = sum(outcome_list) if len(outcome_list)!=0 else 0
net = income - outcome
return render(request,'app/index.html',locals())
def settings(request):
return render(request,'app/settings.html',{})
```
## ==settings.html==
```htmlmixed=
{% extends 'app/base.html' %}
{% block content %}
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="/">
<span data-feather="home"></span>
帳務總覽<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/settings">
<span data-feather="file"></span>
設定
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3">
<h1 class="page-header">分類設定</h1>
</div>
<table class="table table-scrollable">
<thead>
<tr>
<th>分類</th>
</tr>
</thead>
<tbody>
<td>
</td>
</tbody>
</table>
</main>
</div>
</div>
{% endblock %}
```
:::warning
> ```
> <tbody>
> {% for category in categories %}
> <td>{{ category.category }}</td>
> {% endfor %}
> </tbody>
> ```
> ![](https://i.imgur.com/Fr3e2CI.png)
> ![](https://i.imgur.com/O5eI90v.png)
:::
## Form - 利用 HTML 表單與網頁產生互動
* 透過操作表單讓使用者可以新增、刪除資料庫內的資料,並讓網頁產生改變。
### ==settings.html==
```htmlmixed=
{% extends 'app/base.html' %}
{% block content %}
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="/">
<span data-feather="home"></span>
帳務總覽<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/settings">
<span data-feather="file"></span>
設定
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex flex-wrap justify-content-between flex-md-nowrap align-items-center pt-3 pb-2 mb-3">
<h1 class="page-header">分類設定</h1>
</div>
<form action="/add_category" method="post">
{% csrf_token %}
<input type="text" name="add_category_name">
<input type="submit" value="新增分類" class="btn">
</form>
<br/>
<table class="table table-scrollable">
<thead>
<tr>
<th>分類</th>
</tr>
</thead>
<tbody>
{% for category in categories %}
<td>
<div class="col-8 .col-sm-6">{{ category.category }}</div>
<div class="col-8 .col-sm-6"><a href="/delete_category/{{ category.category }}">刪除</a></div>
</td>
{% endfor %}
</tbody>
</table>
</main>
</div>
</div>
{% endblock %}
```
## ==urls.py==
```python=
om django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.frontpage),
url(r'^settings$', views.settings),
url(r'^add_category$', views.addCategory),
url(r'^delete_category/(?P<category>\w+)',views.deleteCategory)
]
```
## ==views.py==
```python=
from django.shortcuts import render, HttpResponse, redirect
from .models import Record, Category
# Create your views here.
def hello(request):
return render(request,'app/example_hello.html',{})
def frontpage(request):
records = Record.objects.filter()
income_list = [record.cash for record in records if record.balance_type == '收入']
outcome_list = [record.cash for record in records if record.balance_type == '支出']
income = sum(income_list) if len(income_list)!=0 else 0
outcome = sum(outcome_list) if len(outcome_list)!=0 else 0
net = income - outcome
return render(request,'app/index.html',locals())
def settings(request):
categories = Category.objects.filter()
return render(request,'app/settings.html',locals())
def addCategory(request):
if request.method == 'POST':
posted_data = request.POST
category = posted_data['add_category_name']
Category.objects.get_or_create(category=category)
return redirect('/settings')
def deleteCategory(request,category):
Category.objects.filter(category=category).delete()
return redirect('/settings')
```
![](https://i.imgur.com/e9c2ktn.png)
# Step7:Form - 利用 Django 表單物件替代 HTML 表單(使用 ModelForm)
![](https://i.imgur.com/WbyakKS.png)
## chia_app 底下新增 ==forms.py==
* forms.py 路徑:/home/testchia/myproject_test/chia_app/forms.py
```python=
from django.forms import ModelForm
from .models import Record
class RecordForm(ModelForm):
class Meta:
model = Record
fields = ['date','description','category','cash','balance_type']
```
## ==index.html==
```htmlmixed=
{% extends 'app/base.html' %}
{% block content %}
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="/">
<span data-feather="home"></span>
帳務總覽<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/settings">
<span data-feather="file"></span>
設定
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="page-header">帳務總覽</h1>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="row placeholders">
<div class="col-xs-8 col-sm-4 placeholder">
<h3>收入</h3>
<span class="text-muted"><h2>{{ income }}</h2></span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h3>支出</h3>
<span class="text-muted"><h2><font color="#DF565C">{{ outcome }}</font></h2></span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h3>存款</h3>
<span class="text-muted"><h2><font color="#53DF7D">{{ net }}</font></h2></span>
</div>
</div>
</div>
</main>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h2 class="sub-header">新增紀錄</h3>
</div>
<div class="table-responsive">
<form action="/add_record" method="post">
{% csrf_token %}
<table class="table table-striped">
<col style="width:15%">
<col style="width:35%">
<col style="width:20%">
<col style="width:18%">
<col style="width:7%">
<thead>
<tr>
<th>日期</th>
<th>描述</th>
<th>分類</th>
<th>金額</th>
<th>收支</th>
</tr>
</thead>
<tbody>
<tr>
{% for field in record_form %}
<td>{{ field }}</td>
{% endfor %}
</tr>
</tbody>
</table>
<div class="right-area">
<input type="submit" class="btn show-new-item" value="新增紀錄" />
</div>
</form>
</div>
</main>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<h2 class="sub-header">歷史記錄</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>日期</th>
<th>描述</th>
<th>分類</th>
<th>金額</th>
<th>收/支</th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr>
<td>{{ record.date | date:"Y-m-d"}}</td>
<td>{{ record.description }}</td>
<td>{{ record.category }}</td>
<td>{{ record.cash }}</td>
<td>{{ record.balance_type }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</main>
</div>
</div>
{% endblock %}
```
## ==dashboard.css==
```css=
.right-area {
float: right;
z-index: 2;
vertical-align: middle;
display: inline-block;
margin-top: -15px;
}
.left-area {
float: left;
margin-left: 3px;
z-index: 2;
vertical-align: middle;
display: inline-block;
margin-top: -15px;
}
form input {
width: 100%;
}
```
## ==views.py==
```python=
from django.shortcuts import render, HttpResponse, redirect
from .models import Record, Category
from .forms import RecordForm
# Create your views here.
def hello(request):
return render(request,'app/example_hello.html',{})
def frontpage(request):
record_form = RecordForm(initial={'balance_type':'支出'})
records = Record.objects.filter()
income_list = [record.cash for record in records if record.balance_type == '收入']
outcome_list = [record.cash for record in records if record.balance_type == '支出']
income = sum(income_list) if len(income_list)!=0 else 0
outcome = sum(outcome_list) if len(outcome_list)!=0 else 0
net = income - outcome
return render(request,'app/index.html',locals())
def settings(request):
categories = Category.objects.filter()
return render(request,'app/settings.html',locals())
def addCategory(request):
if request.method == 'POST':
posted_data = request.POST
category = posted_data['add_category_name']
Category.objects.get_or_create(category=category)
return redirect('/settings')
def deleteCategory(request,category):
Category.objects.filter(category=category).delete()
return redirect('/settings')
```
![](https://i.imgur.com/Ma8r36A.png)
# 補充:跳出月曆,選擇日期
> jQuery CDN:https://code.jquery.com/
> * 複製 minified 並貼到 ==base.html== 下方:
> bootstrap datepicker cdn:https://cdnjs.com/libraries/bootstrap-datepicker
> * 複製 ```https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js``` 並貼到 base.html 下方
>
> ```html=
> ...
> {% block content %} {% endblock %}
> <!-- Bootstrap core JavaScript ================================================== -->
> <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
> <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.8.0/js/bootstrap-datepicker.min.js"></script>
> <script>
> $(function(){
> $('#datepicker1').datepicker({
> format:'yyyy-mm-dd'
> });
> })
> </script>
> </body>
> </html>
> ```
>
> ==forms.py==
> ```python=
> from django.forms import ModelForm, TextInput
> from datetime import date
> from .models import Record
>
> class RecordForm(ModelForm):
> class Meta:
> model = Record
> fields = ['date','description','category','cash','balance_type']
> widgets = {
> 'date':TextInput(
> attrs = {
> 'id':'datepicker1',
> 'value':date.today().strftime('%Y-%m-%d')
> }
> )
> }
> ```
## ==views.py==
```python=
from django.shortcuts import render, HttpResponse, redirect
from .models import Record, Category
from .forms import RecordForm
# Create your views here.
def hello(request):
return render(request,'app/example_hello.html',{})
def frontpage(request):
record_form = RecordForm(initial={'balance_type':'支出'})
records = Record.objects.filter()
income_list = [record.cash for record in records if record.balance_type == '收入']
outcome_list = [record.cash for record in records if record.balance_type == '支出']
income = sum(income_list) if len(income_list)!=0 else 0
outcome = sum(outcome_list) if len(outcome_list)!=0 else 0
net = income - outcome
return render(request,'app/index.html',locals())
def settings(request):
categories = Category.objects.filter()
return render(request,'app/settings.html',locals())
def addCategory(request):
if request.method == 'POST':
posted_data = request.POST
category = posted_data['add_category_name']
Category.objects.get_or_create(category=category)
return redirect('/settings')
def deleteCategory(request,category):
Category.objects.filter(category=category).delete()
return redirect('/settings')
def addRecord(request):
if request.method == 'POST':
form = RecordForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
def deleteRecord(request):
if request.method == 'POST':
id = request.POST['delete_val']
Record.objects.filter(id=id).delete()
return redirect('/')
```
## ==index.html==
```htmlmixed=
{% extends 'app/base.html' %}
{% block content %}
<div class="container-fluid">
<div class="row">
<nav class="col-md-2 d-none d-md-block bg-light sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="/">
<span data-feather="home"></span>
帳務總覽<span class="sr-only">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/settings">
<span data-feather="file"></span>
設定
</a>
</li>
</ul>
</div>
</nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="page-header">帳務總覽</h1>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="row placeholders">
<div class="col-xs-8 col-sm-4 placeholder">
<h3>收入</h3>
<span class="text-muted"><h2>{{ income }}</h2></span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h3>支出</h3>
<span class="text-muted"><h2><font color="#DF565C">{{ outcome }}</font></h2></span>
</div>
<div class="col-xs-8 col-sm-4 placeholder">
<h3>存款</h3>
<span class="text-muted"><h2><font color="#53DF7D">{{ net }}</font></h2></span>
</div>
</div>
</div>
</main>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h2 class="sub-header">新增紀錄</h3>
</div>
<div class="table-responsive">
<form action="/add_record" method="post">
{% csrf_token %}
<table class="table table-striped">
<col style="width:15%">
<col style="width:35%">
<col style="width:20%">
<col style="width:18%">
<col style="width:7%">
<thead>
<tr>
<th>日期</th>
<th>描述</th>
<th>分類</th>
<th>金額</th>
<th>收支</th>
</tr>
</thead>
<tbody>
<tr>
{% for field in record_form %}
<td>{{ field }}</td>
{% endfor %}
</tr>
</tbody>
</table>
<div class="right-area">
<input type="submit" class="btn show-new-item" value="新增紀錄" />
</div>
</form>
</div>
</main>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-4">
<h2 class="sub-header">歷史記錄</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<col style="width:18%">
<col style="width:27%">
<col style="width:20%">
<col style="width:18%">
<col style="width:7%">
<col style="width:5%">
<thead>
<tr>
<th>日期</th>
<th>描述</th>
<th>分類</th>
<th>金額</th>
<th>收/支</th>
<th></th>
</tr>
</thead>
<tbody>
{% for record in records %}
<tr>
<td>{{ record.date | date:"Y-m-d"}}</td>
<td>{{ record.description }}</td>
<td>{{ record.category }}</td>
<td>{{ record.cash }}</td>
<td>{{ record.balance_type }}</td>
<td>
<form method="post" action="/delete_record">
{% csrf_token %}
<input type="hidden" value="{{ record.id }}" name="delete_val">
<input type="submit" class="btn" value="刪除" />
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</main>
</div>
</div>
{% endblock %}
```
## myproject_test 底下的 ==urls.py==
```python=
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.frontpage),
url(r'^settings$', views.settings),
url(r'^add_category$', views.addCategory),
url(r'^delete_category/(?P<category>\w+)',views.deleteCategory),
url(r'^add_record$', views.addRecord),
url(r'^delete_record$', views.deleteRecord),
]
```
# Step8:使用者登入與登出
![](https://i.imgur.com/0L6oQvA.png)
![](https://i.imgur.com/Ip8Fgn5.png)
![](https://i.imgur.com/5VaikbM.png)
![](https://i.imgur.com/sxxX48F.png)
## ==views.py==
```python=
from django.shortcuts import render, HttpResponse, redirect
from .models import Record, Category
from .forms import RecordForm
from django.contrib.auth.decorators import login_required
# Create your views here.
@login_required
def hello(request):
return render(request,'app/example_hello.html',{})
@login_required
def frontpage(request):
record_form = RecordForm(initial={'balance_type':'支出'})
records = Record.objects.filter()
income_list = [record.cash for record in records if record.balance_type == '收入']
outcome_list = [record.cash for record in records if record.balance_type == '支出']
income = sum(income_list) if len(income_list)!=0 else 0
outcome = sum(outcome_list) if len(outcome_list)!=0 else 0
net = income - outcome
return render(request,'app/index.html',locals())
@login_required
def settings(request):
categories = Category.objects.filter()
return render(request,'app/settings.html',locals())
@login_required
def addCategory(request):
if request.method == 'POST':
posted_data = request.POST
category = posted_data['add_category_name']
Category.objects.get_or_create(category=category)
return redirect('/settings')
@login_required
def deleteCategory(request,category):
Category.objects.filter(category=category).delete()
return redirect('/settings')
@login_required
def addRecord(request):
if request.method == 'POST':
form = RecordForm(request.POST)
if form.is_valid():
form.save()
return redirect('/')
@login_required
def deleteRecord(request):
if request.method == 'POST':
id = request.POST['delete_val']
Record.objects.filter(id=id).delete()
return redirect('/')
```
## ==myproject_test 中的 urls.py==
```python=
"""project_chia URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.9/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url, include
from django.contrib import admin
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^', include('chia_app.urls')),
url(r'^accounts/login', auth_views.login),
url(r'^accounts/logout', views.logout),
]
```
## 新增 registration 資料夾,並放入 ==login.html==
```
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test$ cd chia_app
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app$ ls
admin.py forms.py migrations __pycache__ templates urls.py
apps.py __init__.py models.py static tests.py views.py
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app$ cd templates
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app/templates$ ls
app
(py3env) testchia@fengchia-swift-sf314-52:~/myproject_test/chia_app/templates$ mkdir registration
```
### ==login.html==
```htmlmixed=
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>accounts demo</title>
<!-- Bootstrap core CSS -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<!-- Custom styles for this template -->
<link href="{% static 'css/dashboard.css' %}" rel="stylesheet">
<style>
td {
padding: 5px
}
</style>
</head>
<body>
<nav class="navbar-dark bg-dark shadow fixed-top">
<center><h1 class="navbar-brand col-sm-9">My 帳本</h1></center>
</nav>
{% if form.errors %}
<p>您的帳號或密碼有誤。</p>
{% endif %}
{% if next %}
{% if user.is_authenticated %}
<p>您沒有權限造訪此網頁。</p>
{% else %}
<p>請先登入。</p>
{% endif %}
{% endif %}
<form method="post" action="/accounts/login">
{% csrf_token %}
<table align="center" style="margin-top:100px">
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
<tr>
<!--我是next:{{ next }}-->
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="login" class="btn small-button" />
</td>
</tr>
</table>
<input type="hidden" name="next" value="{{ next }}" />
</form>
</body>
</html>
```
## myproject_test 底下新增 ==views.py==
```python=
from django.contrib import auth
from django.shortcuts import redirect
def logout(request):
auth.logout(request)
return redirect('/')
```
# Step9:挑戰關卡-使用者專屬內容
![](https://i.imgur.com/H1c3ODT.png)
* 影片:https://drive.google.com/file/d/19LFP_btgpI9KpLA4wOZszbFe81BYRP3K/view
# Step10:[上傳專案到github](https://drive.google.com/open?id=1OMARgIYtAC5owiHQMKTC_yGFUYuRgbhW)、[部署專案至Python anywhere](https://drive.google.com/open?id=1gbJJxQj0yRJ9J9I693SBQAs3euM3tfNJ)