--- 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)