2023年10月31日 星期二

菁菁出院

菁菁 10/23 住院清創與植皮, 今天下午辦出院, 住院 8 天醫藥費與病房費合計 36596 元, 單人病房每日 3500, 8 天 28000, 其餘 8596 元為醫事處置費. 早上劉醫師來查房時說傷口復原情形很好 (可能每天喝鱸魚湯的原因), 下周一回診. 菁菁的保單醫療部分應可大約 cover, 但 20 年前的日支金額在通 貨膨脹下已縮水, 最近要找經紀人來重新檢視補強. 71 病房最近有一批樹人的實習生, 跟菁菁很談得來, 得空時常跑來跟她聊天, 早上菁菁訂了 16 杯飲料請她們, 我從急診門口提過來還真重啊. 

蝦皮購買小霸王NMK99 ESP32+ESP32CAM 二合一開發版

昨天在臉書看到小霸王尤博士又推出新板子 NMK99, 集 ESP32 與 ESP32-CAM 優點於一身, 雖然我尚未真正拿來應用, 但上回燒錄程式時發現, ESP32-CAM 能用來做 IO 的腳所剩無幾, 這塊 NMK99 應該就是要解決此問題. 用友情價 299 元先買兩塊試試 :





299*2 + 45=643 元, 第一次用店到店, 取貨地址 : 高雄市左營區新下街98 號. 

2023年10月30日 星期一

2023 年第 43 周記事

時序已來到十月底, 自從雙十節過後因為菁菁發生小車禍, 這半個月來生活偏離常軌, 上周一 10/23 去榮總辦住院手術清創右腳踝燙傷部位, 還好這周水某的計畫延遲, 可在病房與辦公室兩邊跑, 我則下班時煮好鱸魚湯與牛排帶過去. 住了一周劉醫師說明天可出院. 工作上則遇到對法律見解不同所引發的執行爭議, 總之 10 月的下半段實在是不順利啊! 

週六 (10/27) 老同學峰大師夫妻與仲仔來鄉下爬九芎林山, 我因還要去市場買菜與到觀音廟給阿蘭上香念咒, 所以就沒參加 (後來得知只有三人去爬, 吳董夫妻只趕上吃午飯, 又覺得後悔沒去, 但我時間太趕沒辦法). 但他們下午來我家暢聊一下午, 度過了愉快的午後. 

最近 AI 的內訓課程嘎然而止, 可能老師疲倦了, 學生也累了, 紅了快一年的 AI 現在可能沒人想聽, 我也是回歸到傳統機器學習, 從根本重新學起. 沒工作時就去上公司購買的 Hahow 企業版, 最近上完 Javascript 與 Bootstrap, 收穫頗豐, 但沒時間寫筆記, 複習時再來整理. Django 學習已到末尾, 剩 Session 與 Cookie, 學完實作幾個書中範例就功成圓滿, 準備在 Mapleboard 上架自己的網站 (我覺得 Bootstrap 很不錯). 

2023年10月28日 星期六

拔牙記

前天晚上睡到半夜被牙痛痛醒, 右下邊的智齒突然痛起來, 起床到冰箱翻找普拿疼沒找到, 只好上網搜尋穴道止痛法, 因之前看針灸術時對穴道止牙疼有印象, 找到下面這篇 :


我用電針對頰車, 下關, 合谷, 與內關等穴, 大約 5 分鐘左右就緩解, 10 分鐘左右就不太感覺痛了, 實在神奇. 我覺得頰車應該最速效, 因為它離痛點的智齒最近, 書上說此穴為牙齒麻醉穴位. 昨天上班時還特地到樓上找學長王醫師確認下關與頰車的正確位置, 頰車是咬牙時兩側下顎骨後部有塊小肉突出處, 用電針正確取穴時附近肌肉會抖動; 下關則是在張嘴時耳朵旁下顎骨與耳骨交叉處的下凹點. 

雖然穴道有止痛效果, 但只能暫時止痛而已, 蛀掉的智齒問題還在, 早上起床發現智齒雖不會痛, 但咬合時感覺浮浮的會痛, 決定還是去找洪醫師拔牙. 因為沒預約, 等了半小時才輪到, 打了麻藥確認無痛感後, 洪醫師開始夾住智齒拔牙, 不到三分鐘就搞定了, 我以為智齒這麼大顆要費很大勁. 洪醫師問我要不要留牙齒, 我說好啊, 小時候祖母說掉的牙下面的丟屋頂, 上面的丟床下, 說將來新齒才會長得好, 雖然這年紀只有掉牙不長牙了. 

2023年10月27日 星期五

Win 11 如何開啟裝置管理員

自從筆電升版為 Win 11 後有些不習慣, 尤其是找不到裝置管理員, 我找到下面這篇 :


這麼多方法實在眼花繚繞, 其實最快最簡單的方法是按 Windows 鍵 + X, 就會跳出一張選單, 裡面就有裝置管理員這個選項, 參考 :





一招就解決了, 哪裡需要這麼多方法. 


2023年10月26日 星期四

Python 學習筆記 : Django 4 用法整理 (四) 資料庫存取

終於來到 Django 的重頭戲~資料庫了, 這是建立資料庫驅動網站的基礎, 我之前學習 Django 2/3 時就是停在這裡, 參考 : 


本次複習的前三篇筆記參考 :



一. 透過 ORM 間接操作資料庫 : 

Django 使用 ORM (Object Relational Mapper) 物件關聯對映機制來操作資料庫, 開發者並非直接使用 SQL 來存取資料庫, 而是使用 Python 模型物件的方法來存取, 具體而言, 就是利用  django.db.models.Model 類別來定義資料表欄位的模型, 以模型做為程式與資料庫之間的中介層, 然後透過 ORM 機制將對 Model 物件之存取轉換為 SQL 指令, 如下圖所示 :




藉由 ORM 將資料庫處理作業抽象化後, 開發者就無須面對 SQL 語言, 只要單純使用 Python 物件就可以間接操作資料表, 因為 ORM 機制會將物件操作轉換成存取後台資料庫的 SQL 指令. ORM 的模型與實體資料庫的對映如下表 : 


 ORM 模型 關聯式資料庫
 類別 (Class) 資料表 (Table)
 物件 (Object) 紀錄 (Row)
 屬性 (Attribute) 欄位 (Field)
 方法 (Method) CRUD 操作


這種作法最大的好處是更換資料庫系統時只要編輯專案設定檔 settings, 將 DATABASE 變數 (字典) default 鍵的值改為要使用的資料庫即可, 預設是 SQLite :

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

連線 MySQL 資料庫的設定範例如下 :

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mydatabase',
        'USER': 'root',
        'PASSWORD': 'mypassword',
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

連線 Postgre 資料庫的設定範例如下 :

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'mydatabase', 
        'USER': 'tony1966', 
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1', 
        'PORT': '5432',
    }
}

這些正式運營使用的伺服器型資料庫系統主要差異是為 ENGINE 與 PORT 這兩個鍵. 

參考 : 


Django 預設使用單檔案的 SQLite 資料庫, 用 Python 存取 SQLite 資料庫必須學習 SQL 語法, 參考 :



二. 建立資料庫驅動的網站 : 

要在 Django 專案中使用資料庫必須先建立 App, 在每個 App 目錄下都會自動建立與資料庫操作相關的檔案與資料夾.   

1. 建立網站 : 

首先以下列指令建立一個專案 mysite :

django-admin startproject mysite  

D:\django\test4>django-admin startproject mysite 

用 tree /f 指令檢視檔案目錄結構 : 

D:\django\test4\mysite>cd .. 
D:\django\test4>tree /f 

mysite
    │  manage.py
    │
    └─mysite
            asgi.py
            settings.py
            urls.py
            wsgi.py
            __init__.py

可見這時並無資料庫相關檔案. 


2. 建立應用程式 : 

接下來建立第一個應用程式 myapp1 為例 : 

python manage.py startapp myapp1 

D:\django\test4\mysite>python manage.py startapp myapp1

用 tree /f  指令檢視檔案目錄結構 : 

D:\django\test4\mysite>cd .. 
D:\django\test4>tree /f 

mysite
    │  manage.py
    │
    ├─mysite
    │  │  asgi.py
    │  │  settings.py
    │  │  urls.py
    │  │  wsgi.py
    │  │  __init__.py
    │  │
    │  └─__pycache__
    │          __init__.cpython-310.pyc
    │          settings.cpython-310.pyc
    │
    └─myapp1
        │  admin.py  
        │  apps.py
        │  models.py   
        │  tests.py
        │  views.py
        │  __init__.py
        │
        └─migrations  
                __init__.py

可見已經新增了應用程式目錄 myapp1, 裡面與資料庫有關的是 admin.py, models.py, 以及 migrations 子目錄, 其中 admin.py 用來把 App 註冊到內建的後台資料庫管理網站 admin 中; models.py 用來定義資料表的模型; 而 migrations 子目錄則用來記錄 migration (模型異動) 的資料檔. 

建立應用程式後首先必須開啟專案設定檔 settings.py, 在 INSTALLED_APPS 變數中註冊此 App, 這動作很重要, 因為後續要生成 migration  (模型異動) 資料檔時, Django 才會知道要去哪些 App 底下找 models.py 模型檔來比對資料表的變化情形 :

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp1', 
]


3. 定義資料表的模型 : 

App 目錄下的 models.py 用來定義與實體資料表對映的資料模型, 其預設內容只有兩列, 已預先匯入 django.db.models 模組, 只要在底下編輯各資料表的模型類別定義即可 : 

from django.db import models    

# Create your models here.

建立資料表模型須繼承 django.db.models.Model 類別, 然後呼叫 models 模組的欄位定義函式 models.xxxField() 建立欄位變數即可, 語法結構如下 :

from django.db import models

class table_name(models.Model):
    field_name_1=models.xxxField(param=value)
    field_name_2=models.xxxField(param=value)
    ...

    class Meta:
         ordering=('-field_name_x', )
         ...
    
    def __str__(self):
        return self.field_name_x   

其中方法 __str__() 為必須, 用來傳回要在內建的資料庫後台應用程式 admin 中顯示的資料表欄位; 子類別 Meta 則可有可無, 用來指定與資料庫相關之屬性, 常用的 Meta 類別屬性如下表 :

 
 Meta 類別屬性 說明
 ordering 用 tuple 指定排序欄位 (預設為升冪, 冠 - 為降冪)
 db_table 指定映射之資料表名稱, 預設自動映射至 '應用名_模型名' 資料表'
 app_label 指定此模型類別所屬之 App 名稱
 default_permissions 指定允許之模型操作許可權, 預設為 ('add', 'change', 'delete')
 get_latest_by 依指定之欄位排序以獲得模型之開始或結束紀錄 (日期或整數欄位) 
 unique_together 將多個欄位設定成不重複的欄位組合 (雙層串列, 例如 [['欄1', '欄3']])
 index_together 設定聯合索引之欄位 (雙層串列, 例如 [['欄1', '欄3']])
 required_db_vendor 限定模型維護之底層資料庫類型, 例如 ['SQLite', 'MySQL']
 db_tablespace 映射之表格空間名稱 (僅適用於如 Oracle 等有表格空間之資料庫)
 required_db_features 指定底層資料庫必須具備之特性, 例如 ['gis_enabled']
 order_with_respect_to 設定此模型可依據指定之外鍵引用關係排序
 

其中最常用的是 ordering 屬性, 用來指定資料庫傳回紀錄時要依據那些欄位來排序, 其值為一個欄位名稱組成的 tuple, 預設為升冪, 欄位名稱前面冠 '-' 則為降冪, 例如 ordering=(name, -birthday) 表示要先按欄位 name 升冪排序, 再按 birthday 欄位降冪排序. 

django.db.models 模組提供了許多欄位定義函式, 其名稱格式為 xxxField(), 如下表所示 : 


 欄位型態函式 說明
 BooleanField() 儲存布林值=True/False, 必要參數 : 無, checkbox 輸入用
 CharField() 儲存單行文字輸入內容,必要參數 :
 max_length=最大字元數 (上限 254)
 SlugField 與 CharField() 相同, 但用來儲存 URL 的一部分
 TextField() 儲存多行文字輸入 textarea 內容, 必要參數 : 無
 IntegerField() 儲存整數 (-2147483648 ~ 2147483647), 必要參數 : 無
 BigInteger() 儲存長度 64 位元的大整數
 PositiveIntegerField() 儲存正整數 (0 ~ 2147483647), 必要參數 : 無
 DecimalField() 儲存固定精度之十進位數 (Decimal 物件), 必要參數 :
 max_digits=最大位數
 decimal_places=整數位數
 FloatField() 儲存浮點數, 參數 : 無
 DateField() 儲存日期 (即 datetime.date), 可選參數 : 
 auto_now=物件儲存時自動儲存今日日期
 auto_now_add=只在建立時儲存今日日期
 DateTimeField() 儲存日期時間 (即 datetime.datetime), 可選參數 : 
 auto_now=物件儲存時自動儲存今日日期時間
 auto_now_add=只在建立時儲存今日日期時間
 EmailField() 儲存有效之電子郵件, 可選參數 :
 max_length=最大字元數 (上限 254)
 FileField() 檔案上傳欄位, 必要參數 : 無
 ImageField() 圖檔欄位 (繼承自 FileField, 須配合使用 Pillow 套件)
 URLField() 儲存完整的 URL (繼承自 CharField), 可選參數 :
 max_length=最大字元數 (預設 200)
 AutoField() 自動增量主鍵欄位, 必要參數 primary_key=True
 ForeignKey() 關聯欄位, 用來指向其他資料表的主鍵 (預設=id) :
 第一參數=所指之資料表類別名稱
 第二參數 : on_delete=models.CASCADE


呼叫上表中的欄位定義函式時除了必要參數外, 還可以傳入選項參數, 這些參數都是對映到 SQL 語法的欄位屬性, 常用選項參數如下表 :


 欄位選項參數 說明
 null 欄位值是否可為 null, 值=True/False (預設)
 blank 欄位值是否可為空白, 值=True/False (預設)
 default 欄位預設值 (或可呼叫物件)
 unique 欄位值是否為唯一, 值=True/False (預設)
 primary_key 欄位是否為主鍵, 值=True/False (預設)
 editable 欄位是否顯示於 admin 後台, 值=True (預設) /False 
 choices 設定 select 欄位之選項 (可用 list 或 tuple)
 help_text 顯示於表單元件上的額外資訊
 verbose_name 欄位之人類可讀名稱, 未指定以欄位名稱代替 (底線變空白)


例如定義一個會員資料表 Members 的模型 : 

from django.db import models

class Members(models.Model):
    name=models.CharField(max_length=20, null=False)
    gender=models.CharField(max_length=2, default='男', null=False)
    birthday=models.DateField(null=False)
    email=models.CharField(max_length=100, blank=True, default='')
    phone=models.CharField(max_length=50, blank=True, default='')
    address=models.CharField(max_length=255, blank=True, default='')
    
    def __str__(self):
        return self.name

此例定義了一個包含 6 個欄位的資料表模型 Members, 除了 birthday 欄位是日期欄位外, 其餘都是長度不等之單行文字欄位. 

models.py 編輯完成存檔後可先用 check 指令檢查模型設定, 如果顯示 "System check identified no issues" 表示設定正確 :

python manage.py check    

D:\django\test4\mysite>python manage.py check
System check identified no issues (0 silenced).


4. 建立 migration 資料檔與資料庫同步 : 

 models.py 建立在上層專案目錄下先用 makemigrations 指令產生模型遷移檔 :

python manage.py makemigrations     (對全部 App)

也可以指定要對哪個 App 建立遷移檔 : 

python manage.py makemigrations myapp1    (只對指定之 App)

migrations 指令會依據 App 的 models.py 內容在該 App 的 migrations 資料夾下產生遷移檔, 主要是紀錄資料表的結構與版本, 並與 migrations 資料夾下面的前一版本的遷移檔比較模型是否有異動. 如果未指名 App, 則會去專案設定檔找尋全部已註冊的 App 逐一建立遷移檔. 

D:\django\test4\mysite>python manage.py makemigrations
Migrations for 'myapp1':
  myapp1\migrations\0001_initial.py
    - Create model Members  

可見此指令新增了一個建立 Members 模型的 0001_initial.py 遷移檔, 其中 0001 表示版本 (第二次更改模型時版本就會變成 0002), initial 表示異動說明 (初次建立模型).   

這時用 tree /f 觀察網站檔案目錄結構, 會發現上層專案目錄下新增了一個 SQLite 資料庫檔db.sqlite3. 注意, 這是所有 App 共用的資料庫, 只有在建立第一個 App 時會產生, 之後的 App 不會再產生 : 

mysite
    │  manage.py
    │  db.sqlite3    
    │
    ├─mysite
    │  │  asgi.py
    │  │  settings.py
    │  │  urls.py
    │  │  wsgi.py
    │  │  __init__.py
    │  │
    │  └─__pycache__
    │          __init__.cpython-310.pyc
    │          urls.cpython-310.pyc
    │          settings.cpython-310.pyc
    │
    └─myapp1
        │  admin.py
        │  apps.py
        │  models.py
        │  tests.py
        │  views.py
        │  __init__.py
        │
        ├─migrations
        │  │  __init__.py
        │  │  0001_initial.py   
        │  │
        │  └─__pycache__
        │          __init__.cpython-310.pyc
        │
        └─__pycache__
                __init__.cpython-310.pyc
                apps.cpython-310.pyc
                models.cpython-310.pyc
                admin.cpython-310.pyc

開啟 migration 資料檔 0001_initial.py 可知其內容就是 models.py 的資料表欄位定義, 它只是將 models.Model 類別轉成了 Migration 類別, 以便後續執行 migrate 指令時與實體資料庫同步, 其內容如下 : 

# Generated by Django 4.2.4 on 2023-10-22 16:12

from django.db import migrations, models

class Migration(migrations.Migration):
    initial = True
    dependencies = [
    ]
    operations = [
        migrations.CreateModel(
            name='Members',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=20)),
                ('gender', models.CharField(default='男', max_length=2)),
                ('birthday', models.DateField()),
                ('email', models.CharField(blank=True, default='', max_length=100)),
                ('phone', models.CharField(blank=True, default='', max_length=50)),
                ('address', models.CharField(blank=True, default='', max_length=255)),
            ],
        ),
    ]

然後在上層專案目錄下用下列指令讓模型與資料庫同步 :

python manage.py migrate   

此指令會到每一個 App 目錄的 migrations 子目錄下的遷移紀錄檔, 比較前後版本差異後去更改資料表, 這樣便達成模型定義與資料庫同步的目的. 也可以在 migrate 後面指定要同步的 App, 這樣就只會同步該 App :

 python manage.py migrate myapp1

注意, 每次修改 models.py 後都要重新執行 makemigrations 與 migrate 指令才能維持模型與資料庫的同步. 例如 : 

D:\django\test4\mysite>python manage.py migrate  
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, myapp1, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying myapp1.0001_initial... OK
  Applying sessions.0001_initial... OK

可見 migrate 指令會依照 makemigrations 產生的遷移資料檔 0001_initial.py 去同步資料庫. 

如果將 email 欄位長度由 100 改成 120, 再次執行 makemigrations 與 migrate 指令重新進行遷移與同步 : 

D:\django\test4>cd mysite

D:\django\test4\mysite>python manage.py makemigrations
Migrations for 'myapp1':
  myapp1\migrations\0002_alter_members_email.py 
    - Alter field email on members

D:\django\test4\mysite>python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, myapp1, sessions
Running migrations:
  Applying myapp1.0002_alter_members_email... OK

可見在 myapp1/migrations 資料夾下多出了 0002_alter_members_email.py 這個中介檔, 從名稱可知此次是要異動模型中的 email 欄位, 內容如下 :

# Generated by Django 4.2.5 on 2023-10-26 03:19

from django.db import migrations, models

class Migration(migrations.Migration):

    dependencies = [
        ('myapp1', '0001_initial'),  
    ]

    operations = [
        migrations.AlterField(
            model_name='members',
            name='email',
            field=models.CharField(blank=True, default='', max_length=120),
        ),
    ]

Django 會比較 models.py 與最近一版的 migrations 資料檔 (即 dependencies 變數中的 '0001_initial'), 得出要如何做異動 (即 operations 變數之值). 經過 migrate 後, 模型的最新異動就與資料庫 db.sqlite3 同步了. 


5. 使用資料庫後台管理程式 admin : 

Django 內建了一個用來管理後台資料庫的應用程式 admin, 專案路由模組 urls.py 中預設路由 path('admin/', admin.site.urls) 即用來存取此 App, 在瀏覽器輸入http://127.0.0.1:8000/admin 會顯示登入畫面 :




欲進入 admin 後台必須用 createsuperuser 指令設定登入帳密, 先按 Ctrl + C 停止測試伺服器, 然後輸入下列指令進行設定 :

python manage.py createsuperuser   

D:\django\test4\mysite>python manage.py createsuperuser
Username (leave blank to use 'user'): 
Email address: abc@gmail.com
Password:
Password (again):
Superuser created successfully.

用 python manage.py runserver 重新開啟測試伺服器, 然後用上面申請的帳密即可登入 : 




可見後台並未出現 Members 模型的資料表, 這是因為沒有向 admin 註冊此模型之故, 須編輯 App 目錄下的 admin.py 將此模型註冊到 admin 應用程式中才行. admin.py 預設只有一列匯入了 admin 應用程式的指令 :

from django.contrib import admin

# Register your models here.
 
在 admin.py 加入如下指令, 先匯入 myapp1 模型檔 models.py 中的模型類別 Members, 然後呼叫 admin.site.register() 函式註冊此 Members 模型 :  

# admin.py
from django.contrib import admin  
from myapp1.models import Members   # 匯入 Members 模型

admin.site.register(Members)    # 向 admin 註冊 Members 模型

存檔後重新整理網頁就會看到 Members 模型所對應的資料表了 :




可見實體資料庫中的資料表名稱是 Memberss, 與模型名稱 Members 有所不同, 不過因 ORM 作為中介, 開發者不會直接存取實體資料庫, 故實際資料表名稱為何無關緊要.  

按 +Add 可新增資料 : 




按 Save 儲存後顯示使用者列表 : 




此處顯示人名是因為 models.py 中的模型裡 __str__() 方法傳回的是 name 欄位之故 : 

    def __str__(self):
        return self.name

如果要顯示更多欄位, 必須在 admin.py 中定義一個 ModelAdmin 類別的子類別, 在其中定義一個名為 list_display 的顯示欄位 tuple, 而且必須向 admin 註冊, 例如 :

# admin.py 
from django.contrib import admin
from myapp1.models import Members   # 匯入 Members 模型

class MembersAdmin(admin.ModelAdmin):    # 顯示欄位類別
    list_display=('id', 'name', 'gender', 'birthday', 'email', 'phone', 'address')

# 向 admin 註冊 Members, MembersAdmin
admin.site.register(Members, MembersAdmin)     # 顯示欄位類別也要註冊

存檔後重新整理網頁就會取代 __str__() 的顯示單一欄位功能而顯示所指定的多欄位了 : 




可見資料預設是以 id 倒序 (降冪) 排列的, 如果要用升冪排序, 須於 MembersAdmin 類別中定義一個名為 ordering 的 tuple 變數, 其元素為預設排序欄位例如 'id', 如果要降冪排序, 則欄位名稱前面冠 "-" 即可, 例如 '-id' : 

from django.contrib import admin
from myapp1.models import Members   # 匯入 Members 模型

class MembersAdmin(admin.ModelAdmin):
    list_display=('id', 'name', 'gender', 'birthday', 'email', 'phone', 'address')
    ordering=('id', )     # 依照 id 升冪排序

# 向 admin 註冊 Members, MembersAdmin
admin.site.register(Members, MembersAdmin)

重新整理網頁就會按照 id 升冪排序了 : 




注意, 指定排序欄位 id 後面有個向上的小三角形, 表示是升冪排序欄位. 其實點按每個顯示欄位都會按照該欄位升冪或降冪排序, ordering 只是指定預設之排序欄位而已. 

除了 ordering 與 list_display 變數外, ModelAdmin 類別內還可以用 search_fields 變數指定搜尋欄位; 也可以用 list_filter 指定資料過濾欄位, 它們都是 tuple 變數, 例如 : 


from django.contrib import admin
from myapp1.models import Members   # 匯入 Members 模型

class MembersAdmin(admin.ModelAdmin):
    list_display=('id', 'name', 'gender', 'birthday', 'email', 'phone', 'address')
    ordering=('id', )
    search_fields=('name', 'email', 'phone', 'address',)
    list_filter=('name', 'gender')

# 向 admin 註冊 Members, MembersAdmin
admin.site.register(Members, MembersAdmin) 

注意, 並非所有欄位都可以可以放在 list_filter 過濾, 可以過濾的欄位類型只有下面幾種 : 
  • CharField
  • BooleanField
  • DateField
  • DateTimeField 
  • IntegerField
  • ForeignKey
重新整理網頁, 在上方會出現搜尋控件, 可搜尋 search_fields 變數所指定之欄位; 其次右方會出現 list_filter 變數指定之欄位值, 點擊就會顯示依據該值過濾之紀錄列表 : 




以上測試範例檔放在 GitHub :


2023年10月25日 星期三

自行更換機車電池市調

前天 (周一) 要上班時發現我的光陽機車發不動, 有過電但馬達沒帶動引擎點火, 且燈一下子就暗下來, 研判是電瓶電量不足, 只好騎菁菁的機車去上班. 下午辦好菁菁住院手續, 等水某下班來病房後, 就回家把車牽去袁老闆那裏, 小師傅用電表一量, 果然電量過低, 換新一顆 950 元, 加上機油, 齒輪油, 還有後輪胎已磨平也要換 (850 元), 總共花了 2130 元. 

小師傅在換電瓶時我在旁邊觀看, 覺得其實很簡單, 也可以自己換 (換輪胎就沒辦法了, 呵呵), 只要將坐墊下置物箱末端的蓋板上兩顆螺絲打開, 卸下電線, 裝上新電池鎖回去即可. 回家查露天一顆 7A 電池才 499 元而已 : 


如果要量電池電壓電流可買下面這款 : 


但網購電池通常要三天才到, 如果機車急用就沒辦法了. 

LG gram 送修

今天早上要用電腦時發現我的 LG gram 好幾個鍵打不出來, 原以為又是 ScrLock 出問題, 但檢查設定並無問題, 應該是鍵盤模組局部故障, 測試每個鍵, 發現按不出來的有如下數鍵 :

` (backtick, 1 左邊那個小蟲), a, 0, 6, Windows, 以及數字盤上的 1. 打電話給展碁 (07-3352116, 一心一路 243 號 8F-3, 早上 09:00~12:00, 中午 12~13 午休, 下午至 17:40), 描述了大概情形, 客服也說是鍵盤的問題, 天哪, 買來才剛好 2 個月就維修, 我是買到機王嗎? 這讓我對 LG 產品的好印象大打折扣 (家裡的電器大多是 LG). 

把 SSD 資料備份後, 中午午休時沿光華路騎到底至一心一路右轉就到了, 位於企業領袖廣場大樓的 8F 之 3, 客服因要叫料, 大概需 3 天才能完修. 


2023-10-26 補充 :

我上遠傳 Friday 網站詢問我這台是福利機或展示機嗎? 回覆是新機, 還附上建國路欣亞的保固聯絡電話, 要我向他們詢問 :

訂單:20230824835743查詢您購買的為新品,為加速解決您遇到的問題,建議您可先行與廠商進行電話諮詢,服務維修廠商相關資料如下,

訂單:20230824835743查詢您購買的為新品,為加速解決您遇到的問題,建議您可先行與廠商進行電話諮詢,服務維修廠商相關資料如下,

訂單:20230824835743
廠商名稱:欣亞數位股份有限公司
電話:07-2373633

第一次在 Friday 買東西就買到機王, 現在看到 Friday 寄來的行銷信感覺都不一樣了. 

2023-10-27 補充 :

下午接到展碁來電, 說目前鍵盤僅白色有料, 黑色的從韓國訂料要一個月, 問我要不要先把筆電帶回去, 等料到再派人來拿筆電. 原來都是韓國製. 教訓 : 以後還是愛用國貨買雙 A 或 MSI 才不會有這種一個月都沒筆電可用的窘狀

2023-10-28 補充 :

昨天將缺料未修的筆電帶回來, 發現居然之前失效的 A, 1, Window 等鍵居然恢復正常, 明明送修前開機兩次都無法敲出這些鍵, 真的非常詭異啊! 今天早上使用正常, 去拔智齒回來又失效, 但按了幾次又回魂, 若說是這些鍵物理或電氣上有異常, 不可能有時又能用啊! 直覺是 Win 11 的問題, 有可能錯怪 LG 了. 

2023-11-06 :

經過一周來的觀察, 發現 A, 5, 6, 0, Windows 等鍵還是不定時無反應, 應該是鍵盤本身問題, 與新注音無關, 所以早上打電話給展碁還是幫我訂料, 順便詢問可否順便幫我裝第二插槽的 SSD, 回答可以, 但要收 630 元安裝費. 

2023-12-08 :

昨天接到展碁來電, 說黑色鍵盤已到, 我下班拿過去就先回家, 因為他們 17:40 下班, 若我在那邊等變成他必須加班. 今早回電說已換好< 我中午去取, 同時付上回安裝 SSD 的錢. 莊工程師人不錯, 跟我聊了一會兒, 原來 LG 筆電其實也是代工, 我這台亮點就是 LG 螢幕面板 (電池應該也是), 之前有 LG 第一代用戶都用到第 8 年才來修 (但 OS 太舊也該換了吧). 不過他建議以後買筆電最好買雙 A, 因為國外品牌過保零件與維修較貴. 

2023年10月24日 星期二

菁菁住院記

昨天下午請假載菁菁去榮總辦理住院準備右腳踝因車禍燙傷的清創, 還好外科病房還有單人房 (3500 元), 下午先做心電圖與 X 光檢查後, 到麻醉門診做訪談, 醫師說可半身也可全身, 我傾向半身, 因為覺得風險較低, 但菁菁覺得手術時清醒感覺很怪, 堅持要麻全身. 

晚上劉主任來病房, 說若清創時傷口可以的話做完接著植皮, 這樣就不用麻兩次, 原訂住院 12~14 天也可縮短至大約 5 天. 今早 10 點進手術房, 到 13:40 才從恢復室出來回病房. 雖然只是小手術, 但在恢復室外面等待的時間真煎熬, 因不時要抬頭看顯示幕的進度, 心情很是忐忑. 上一次來這裡是四年前婷婷表妹剖腹產, 那時小舅一直找我講話來掩飾內心的焦急. 

等回到病房才覺得飢腸轆轆, 把水某帶來已冷掉的員工餐吃完快三點了, 就先回公司上班, 如果水某要收案子我再請假. 這次菁菁的無妄之災也讓我重新審視小狐狸們的保險, 雖然出生時都有保險, 醫療部分也還能 cover, 但 20 年過去保障內容也有落差 (通貨膨脹), 需要找一下經紀人秀珠來提加保意外險與加買醫療險的建議書. 

2023年10月22日 星期日

2023 年第 42 周記事

由於榮總的劉醫師上周要去花蓮開會, 所以菁菁右腳踝燙傷清創排到下周一住院處理, 這周她說要去工作室上班, 說在家太無聊且只是坐著接睫毛, 傷好後工作會爆棚很難排時間, 所以我這幾天都載菁菁去楠梓的工作室後再去上班, 以前對楠梓的路不太熟, 幾天下來大致知道東西南北.

由於可能下周無法回鄉下, 所以週六早上載菁菁去工作室後順路回鄉下準備爸下周的晚餐便當. 前幾天看到社區廚師入圍廚藝比賽, 我問爸社區送餐覺得菜色如何, 他說還不錯, 我週三就跟社區詢問改成送兩餐問題, 答覆是從 11/1 日開始會計較好作帳, 當下便敲定 11/1 起從送中餐改成送中晚餐, 現做的比較新鮮, 這樣以後周末回去就只要準備 2~3 個便當即可.  

這一周仍繼續學習 Django, 我的 Hostinger 主機即將於 10/24 停止運作, 打算在 Mapleboard 上用 Django 架站取代. 另外 Hahow 企業版六角學院的 Bootstrap 4 也分段看完了 (約 9 小時), 讓我對 Bootstrap 刮目相看, 雖然之前就學過了, 但有老師講解還是不同, 懂眉角就能輕易設計出美美的前端. 

購買小米 GaN 65W 充電器

週五下班回家要用筆電時才發現充電線忘記帶回來, 晚上去 YAMAHA 牽菁菁的機車時繞去公司拿回充電線, 回程直接去裕誠路小米店買 65W 筆電充電器, 卻剛好賣出缺貨, 店員說要下周四才會進貨, 要我屆時打電話預留. 回來查小米官網就有貨, 一個 715 元 : 


原本小米線上購要滿 800 元才免運費, 但最近一周有免運券活動, 所以就直接訂購一個, 不用再跑一趟小米店了 : 





其實八月底買 LG gram 16 時就有想要買個備用充電器, 但一直沒時間去小米店看看. 偶而忘記拔電源線會很麻煩, 如果是在高雄家裡還好, LG gram 撐個 10 小時沒問題, 但如是是從鄉下回高雄時忘了拔充電器, 那接下來一周就麻煩了, 所以背包裡一定要放兩個充電器.

參考 : 



2023-10-24 補充 :

今天到貨 (從楊梅電研路 68 號 4 樓寄來, 原來台灣小米在楊梅), 確實可充 LG gram 16 筆電, 溫度與原廠差不多. 

2023年10月20日 星期五

Python 學習筆記 : Django 4 用法整理 (三) 靜態檔案

今天繼續整理 Django 4 的用法, 本次複習的前兩篇筆記參考 :


本篇延續前一篇網頁模板的架構, 在此基礎上添加靜態檔案, 例如圖片, 影片, CSS 樣式表, 與 Javascript 程式檔等, 這些資源大部分可以透過 CDN 或從第三方網址載入到客戶端以節省伺服器空間, 但有些情況下必須由網站本身提供 (例如封閉環境無法使用 Internet 或為了使用特定版本的網頁框架等). 

以下測試參考了之前的筆記 :



一. 編輯專案設定檔 settings.py : 

Django 的靜態檔案可以放在 App 目錄下, 也可以放在上層專案目錄下 (需設定 STATICFILES_DIRS 變數). 

首先開啟下層專案目錄 mysite 下的設定檔 settings.py 檢查 INSTALLED_APPS 變數, 看看是否已安裝 django.contrib.staticfiles 這個 App (Django 4 預設已安裝) :

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp1',
]

有了 django.contrib.staticfiles 這個 App 就可以在模板網頁中使用 {% load static %} 與 {% static %} 等靜態檔案指令. 

其次檢查 settings.py 的靜態檔案資料夾名稱變數 STATIC_URL 設定 (預設值為 'static/'): 

STATIC_URL = 'static/' 

這表示 Django 預設會到各 App 目錄底下的 static 資料夾尋找靜態檔案資源. 

如果靜態檔案是放在其他位置 (例如上層專案目錄下的 static), 就必須添加一個 STATICFILES_DIRS 變數來指定, 此變數是一個串列, 只要將靜態檔案目錄之路徑放入串列中即可, 例如上層專案目錄的 static 資料夾位置指定方式如下 : 

STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

其中 BASE_DIR 為網站根目錄 (即上層專案目錄) 絕對位置. 注意, 以前在 Django 2 必須用 os.path.join() 函式將 BASE_DIR 與 'static' 組合起來, 在 Django 4 已經不需要了, 用 / 直接將兩者串起來即可. 

參考官網教學文件 :


在模板網頁中使用靜態檔案之前必須先用下列模板指令載入靜態檔案路徑 (通常放在 head 標籤內匯入 CSS 與 Javascript 之前) : 

{% load static %} 

注意, 在 Django 2 版使用的 {% load staticfiles %} 在 Django 3 版後已不再使用, 改用 {% load static %} 指令. 然後即可用下列模板指令格式載入靜態檔案資源 : 

{% static "靜態檔檔名" %}

注意, 如果靜態檔案是放在 static 的子目錄下, 則必須加上路徑, 例如放在 static/images/ 下的  ok.jpg 要寫成 {% static "images/ok.jpg" %}. 


二. 建立靜態檔案資料夾 static : 

首先從上一篇測試文章 "Python 學習筆記 : Django 4 用法整理 (二) 網頁模板" 下載模板網頁範例來進行修改 :


在上層專案目錄 mysite 底下建立專案靜態檔案資料夾 static (與 App 資料夾平行), 但常用的靜態檔案有圖片, 樣式與程式檔, 全部混在一起很亂, 通常會在 static 下再建立 images, css, 與 js 三個子目錄用來分別存放上面三種常用的靜態檔案, 

建立好專案靜態資料夾 static 後, 將其複製一份到 myapp1 底下 :

mysite\ (上層專案目錄)
     |____ manage.py
     |____ db.sqlite3
     |____ mysite\  (下層專案目錄)
     |              |____ __init__.py
     |              |____ settings.py
     |              |____ urls.py
     |              |____ views.py  (自行添加)
     |              |____ wsgi.py
     |____ templates\ (網頁模板目錄)
     |              |____ home.htm  (網站首頁)
     |____ static\        (網站靜態檔案目錄)
     |              |____ images\
     |              |____ css\
     |              |____ js\
     |____ myapp1\                     (應用程式目錄)
                  |____ migrations\    (資料庫同步)
                   |____ __init__.py    (形成套件)
                 |____ admin.py       (註冊資料模型)
                 |____ apps.py         (App 設定檔)
                 |____ models.py     (資料模型定義)
                 |____ tests.py          (測試用程式)
                 |____ views.py        (App 視圖模組, 自動建立)
                 |____ urls.py           (App 路由模組, 手工添加)
                 |____ templates\     (網頁模板目錄)
                 |             |____ hello.htm  
                 |             |____ myapp1_home.htm  (App 首頁)  
                   |____ static\               (App 靜態檔案目錄)
                                   |____ images\
                                   |____ css\
                                   |____ js\


三. 載入圖檔 : 

首先準備兩個表示 Home 的圖檔來測試能否成功匯入靜態圖檔資源, 第一張是要放在專案網站首頁中的圖檔 home.png : 





下載後將其更名為 home.png 存放到上層專案目錄 mysite 的 static/images 下.

另一張圖則是要給 myapp1 的 App 首頁 myapp1_home.htm 用的 :


原圖解析度為 512x512, 先用 Picpick 轉成跟上面的 home.png 相同的 125x125, 更名為 myapp1_home.png, 存放於 myapp1/static/images 下 : 




此例網站架構圖如下 : 

mysite\ (上層專案目錄)
     |____ manage.py
     |____ db.sqlite3
     |____ mysite\  (下層專案目錄)
     |              |____ __init__.py
     |              |____ settings.py
     |              |____ urls.py
     |              |____ views.py  (自行添加)
     |              |____ wsgi.py
     |____ templates\ (網頁模板目錄)
     |              |____ home.htm  (網站首頁)
     |____ static\        (網站靜態檔案目錄)
     |              |____ images\
     |              |            |____ home.png   
     |              |____ css\
     |              |____ js\
     |____ myapp1\                     (應用程式目錄)
                  |____ migrations\    (資料庫同步)
                   |____ __init__.py    (形成套件)
                 |____ admin.py       (註冊資料模型)
                 |____ apps.py         (App 設定檔)
                 |____ models.py     (資料模型定義)
                 |____ tests.py          (測試用程式)
                 |____ views.py        (App 視圖模組, 自動建立)
                 |____ urls.py           (App 路由模組, 手工添加)
                 |____ templates\     (網頁模板目錄)
                 |             |____ hello.htm  
                 |             |____ myapp1_home.htm  (App 首頁)  
                   |____ static\               (App 靜態檔案目錄)
                                   |____ images\
                                   |             |____ myapp1_home.png   
                                   |____ css\
                                   |____ js\
 
首先修改專案網頁模板資料夾 templates 下的網站首頁 home.htm, 在上面添加靜態檔案資源 : 

<!-- home.htm -->
<!DOCTYPE html>
<html>
<head>
  <title>首頁</title>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  {% load static %}   
</head>
<body>
  <h4>歡迎來到我的首頁! 現在的時間是 {{now}}</h4>
  <img src="{% static "images/home.png" %}">
  <p><a href="/myapp1">到 myapp1</a></p>
</body>
</html>

其中 {% load static %} 指令只要在 {% static "靜態檔檔名" %} 之前即可, 通常是放在 head 標籤內. 圖檔底下多了一個前往 myapp1 首頁的超連結. 

其次編輯 myapp1/templates 下的 App 首頁檔 myapp1_home.htm :

<!-- myapp1_home.htm -->
<!DOCTYPE html>
<html>
<head>
  <title>Hello</title>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  {% load static %}   
</head>
<body>
  <h4>這是 App1 首頁!</h4>
  <img src="{% static "images/myapp1_home.png" %}" alt="回首頁">
  <p><a href="/">回網站首頁</a></p>
</body>
</html>

注意, 此處載入的圖片是 myapp1 的 static 子目錄 images 下的 myapp1_home.png. 存檔後在上層專案目錄下用 python manage.py runserver 啟動測試伺服器, 然後瀏覽 127.0.0.1:8000 即可看到網站首頁 home.htm 會載入圖檔 : 




按底下的 '到 myapp1' 超連結就會顯示 myapp1 的首頁 : 



可見圖檔不論是位於專案還是 App 的 static 資料夾內都能正確地載入網頁中. 

以上測試之網站資料可從 GitHub 下載 : 



四. 載入 CSS 與 Javascript 檔 : 

這兩種靜態檔載入方式與上面的圖檔類似, 只要將 .css 與 .js 檔分別放入 static/css 與 static/js 資料夾下面即可. 以下使用上面範例中 myapp1 的 hello.htm 模板來測試 css 與 js 靜態檔案的載入, 先準備下面兩個檔案 :

/* hello.css  */
.msg {
  font-weight: bold;
  text-shadow: navy 3px 3px 3px;
  }

此 hello.css 放在 myapp1/static/css 資料夾下面. 

/* hello.js */
alert("Hello!");

此 hello.js 放在 myapp1/static/js 資料夾下面.

這是之前測試 Django 2 時所用的簡單範例, 參考 :


此例網站架構圖如下 : 

mysite\ (上層專案目錄)
     |____ manage.py
     |____ db.sqlite3
     |____ mysite\  (下層專案目錄)
     |              |____ __init__.py
     |              |____ settings.py
     |              |____ urls.py
     |              |____ views.py  (自行添加)
     |              |____ wsgi.py
     |____ templates\ (網頁模板目錄)
     |              |____ home.htm  (網站首頁)
     |____ static\        (網站靜態檔案目錄)
     |              |____ images\
     |              |            |____ home.png
     |              |____ css\
     |              |____ js\
     |____ myapp1\                     (應用程式目錄)
                  |____ migrations\    (資料庫同步)
                   |____ __init__.py    (形成套件)
                 |____ admin.py       (註冊資料模型)
                 |____ apps.py         (App 設定檔)
                 |____ models.py     (資料模型定義)
                 |____ tests.py          (測試用程式)
                 |____ views.py        (App 視圖模組, 自動建立)
                 |____ urls.py           (App 路由模組, 手工添加)
                 |____ templates\     (網頁模板目錄)
                 |             |____ hello.htm  
                 |             |____ myapp1_home.htm  (App 首頁)  
                   |____ static\               (App 靜態檔案目錄)
                                   |____ images\
                                   |             |____ myapp1_home.png
                                   |____ css\
                                   |             |____ hello.css  
                                   |____ js\
                                                 |____ hello.js   

然後修改 myapp1/templates 底下的模板網頁 hello.htm, 先利用模板指令載入兩個靜態檔案 (藍色粗體部分), 然後在顯示招呼語的 h4 標籤上添加 class='msg' 樣式 : 

<!-- hello.htm -->
<!DOCTYPE html>
<html>
<head>
  <title>Hello</title>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  {% load static %}  
  <link rel="stylesheet" href="{% static "css/hello.css" %}">
  <script src="{% static "js/hello.js" %}"></script>
</head>
<body>
  <h4 class="msg">Hello, {{name}}, 現在的時間是 {{now}}</h4>
</body>
</html>

這樣就完成全部配置了, 這時瀏覽 127.0.0.1:8000/myapp1/hello/Tony 會因為執行載入的 hello.js 而先跳出一個 'Hello!' 提示視窗, 關掉後再套用載入的 hello.css 顯示字型的陰影效果 : 



可見載入的 js 檔有被執行, css 檔的樣式也套用到 h4 的招呼語上了. 

以上測試範例可從 GitHub 下載 : 


最後來測試能否從上層專案目錄 mysite 下的 static 資料夾載入 css 與 js 檔, 將前面範例的 hello.css 複製到 mysite/static/css 下, 改名為 welcome.css, 內容不變 : 

/* welcome.css  */
.msg {
  font-weight: bold;
  text-shadow: navy 3px 3px 3px;
  }

將前面範例的 hello.css 複製到 mysite/static/js 下, 改名為 welcome.js, 內容則是將 alert() 的傳入參數改為 'Welcome!' :

/* welcome.js */
alert("Welcome!");

此例網站架構圖如下 : 

mysite\ (上層專案目錄)
     |____ manage.py
     |____ db.sqlite3
     |____ mysite\  (下層專案目錄)
     |              |____ __init__.py
     |              |____ settings.py
     |              |____ urls.py
     |              |____ views.py  (自行添加)
     |              |____ wsgi.py
     |____ templates\ (網頁模板目錄)
     |              |____ home.htm  (網站首頁)
     |____ static\        (網站靜態檔案目錄)
     |              |____ images\
     |              |            |____ home.[ng
     |              |____ css\
     |              |            |____ welcome.css    
     |              |____ js\
     |                           |____ welcome.js   
     |____ myapp1\                     (應用程式目錄)
                  |____ migrations\    (資料庫同步)
                   |____ __init__.py    (形成套件)
                 |____ admin.py       (註冊資料模型)
                 |____ apps.py         (App 設定檔)
                 |____ models.py     (資料模型定義)
                 |____ tests.py          (測試用程式)
                 |____ views.py        (App 視圖模組, 自動建立)
                 |____ urls.py           (App 路由模組, 手工添加)
                 |____ templates\     (網頁模板目錄)
                 |             |____ hello.htm  
                 |             |____ myapp1_home.htm  (App 首頁)  
                   |____ static\               (App 靜態檔案目錄)
                                   |____ images\
                                   |             |____ myapp1_home.png
                                   |____ css\
                                   |             |____ hello.css
                                   |____ js\
                                                 |____ hello.js

最後修改網站首頁模板 home.htm, 加上模板指令載入 css 與 js 靜態檔案, 並替 h4 標籤加上 class="msg" 以便讓首頁歡迎語套上 welcome.css 中的 msg 樣式類別 :

<!-- home.htm -->
<!DOCTYPE html>
<html>
<head>
  <title>首頁</title>
  <meta charset="utf-8">
  <meta http-equiv="cache-control" content="no-cache">
  {% load static %}
  <link rel="stylesheet" href="{% static "css/welcome.css" %}">
  <script src="{% static "js/welcome.js" %}"></script>
</head>
<body>
  <h4 class="msg">歡迎來到我的首頁! 現在的時間是 {{now}}</h4>
  <img src='{% static "images/home.png" %}'>
  <p><a href="/myapp1">到 myapp1</a></p>
</body>
</html>

這時瀏覽 127.0.0.1:8000/ 會因為執行載入的 welcome.js 而先跳出一個 'Welcome!' 提示視窗, 關掉提示後再套用載入的 welcome.css 而顯示字型的陰影效果 :




此測試範例可從 GitHub 下載 :