部署 Django 專案時,常見「靜態檔案爆版」問題:明明已經更新了 CSS/JS,使用者卻還是看到舊版畫面。這篇文章將帶你找出問題核心,並給出最佳解法。 問題現象 前端樣式或 JS 更新後,使用者端仍然載入舊檔案 清除瀏覽器快取後才會看到新內容 CDN 或 Nginx 設定了長時間快取 問題核心 Django 預設的 collectstatic 只會把檔案複製到 STATIC_ROOT,檔名不變。如果 Nginx 或 CDN 設定了 cache-control/expires,檔案內容變了但檔名沒變,使用者端就會一直拿到舊檔案。 關鍵: 只要檔名沒變,快取就不會重新抓新檔案。 解決方法 1. 啟用 ManifestStaticFilesStorage Django 內建的 ManifestStaticFilesStorage 會在檔名自動加上 hash,只要內容有變,檔名就會變,快取自然失效。 設定方式 在 settings.py 加入: STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" 2. 正確執行 collectstatic 每次部署靜態檔案有變動時,務必執行: python manage.py collectstatic 這會自動產生帶 hash 的檔案,例如: style.1a2b3c4d.css 3. 模板引用方式 確保模板都用 {% static 'path/to/file.css' %},Django 會自動引用帶 hash 的檔名。 4. Nginx / CDN 快取設定 可以放心設定長快取,例如: location /static/ { alias /staticfiles/; expires 7d; add_header Cache-Control "public"; } 因為檔名有 hash,內容變了檔名也會變,快取不會爆版。 排查流程 確認 collectstatic 是否有執行 檢查 staticfiles 目錄下檔名是否有 hash 檢查模板是否正確用 static 標籤 檢查 Nginx/CDN 是否有快取設定 檢查瀏覽器載入的檔案路徑是否為新檔名 結論 只要啟用 ManifestStaticFilesStorage 並正確部署,Django 靜態檔案快取問題就能徹底解決。 這是現代 Django 專案部署的最佳實踐!

為什麼需要索引? 在關聯式資料庫中,若沒有索引,查詢時必須逐列(row‑by‑row)掃描整張表(全表掃描 / Full Table Scan)。 建立索引就像在書本的目錄貼上標籤,讓資料庫能快速定位資料,降低 I/O 成本,提升查詢效率。 索引的主要類型 類型 主要結構 適用情境 B‑Tree (預設) 平衡搜尋樹 大多數範圍查詢、排序 Hash Index 雜湊表 精準比對 (=) GiST / GIN 特殊索引 文字搜尋、GIS、JSONB Bitmap Index 位元圖 大型資料倉儲、低寫入量,高查詢壓力 不同資料庫支援的索引型別略有差異,請以官方文件為準。 索引的優點 加速查詢 O(log n) 級別搜尋(B‑Tree)代替 O(n) 全表掃描 對高選擇性欄位(distinct 值多)效果最佳 支援唯一性約束(UNIQUE) 防止重複資料,同時利用索引查找衝突 提升排序與分組效率 ORDER BY / GROUP BY 若使用索引涵蓋欄位,可避免額外排序 覆蓋索引(Covering Index) 查詢所需欄位全在索引中,省去回表(回到資料列)的成本 優化關聯 / JOIN 在外鍵、關聯欄位建索引,可顯著降低複雜查詢時間 索引的缺點 寫入放大(Write Amplification) INSERT / UPDATE / DELETE 必須同步更新索引 高頻寫入場景可能因索引過多而拖慢整體效能 額外儲存空間 索引需要保存鍵值與指標,通常佔用資料表數倍空間 SSD 成本高或磁碟資源有限時需要權衡 維護成本 索引碎片化(Fragmentation)導致效能降低 需定期 REINDEX / OPTIMIZE,佔用系統資源 查詢規劃複雜度 不適當或重疊的索引會混淆查詢最佳化器 可能反而導致 sub‑optimal 計劃 鎖定與競爭 部分資料庫在建立/重建索引時需鎖表或消耗大量 CPU 線上重建(Online Rebuild)仍有資源競爭風險 建立索引的最佳實務 以查詢為核心 依實際慢查詢(slow query log)或 EXPLAIN 報告決定索引策略 先優化 schema 與 SQL,再考慮添加索引 選擇性 > 0.2 選擇性(distinct/total rows)太低的欄位通常不該單獨建索引 可考慮複合索引或前綴索引 覆蓋索引優先 常見查詢僅讀取少量欄位時,可設計覆蓋索引取得最大效益 觀察寫入壓力 高寫入 OLTP 系統:少量、精準索引 查詢為主 OLAP / 報表系統:可接受多重索引 定期審計與重建 使用 pg_stat_user_indexes、mysql.innodb_index_stats 等檢視未使用索引 失效或冗餘索引要及時移除 範例:PostgreSQL 建立複合索引 -- 針對 (status, created_at) 的高頻查詢 CREATE INDEX idx_orders_status_created ON orders (status, created_at DESC); 注意:複合索引遵循最左前綴原則,若只查 created_at 而不含 status,此索引將無法被使用。 面試回答 索引的優點有: 提升查詢效率 對於常用在 WHERE、JOIN、ORDER BY 的欄位建立索引,可以大幅降低查詢時間,從全表掃描降到數筆查找。 支援唯一性限制 像是 UNIQUE 或主鍵索引可以避免重複資料。 覆蓋查詢(Covering Index) 若查詢所需欄位都包含在索引中,可直接從索引取得資料,省略回表動作。 索引的缺點有: 寫入效能降低 每次 INSERT、UPDATE、DELETE 都要同步更新相關索引,會增加寫入成本。 占用額外空間 索引是額外資料結構,尤其在高頻查詢、大資料量的系統中,可能需要數倍的空間。 維護成本 索引太多可能會混淆查詢最佳化器(optimizer),導致選錯查詢路徑。也可能隨時間產生碎片,影響效能。 補充實務觀點(加分項): 平常會配合 EXPLAIN 去分析查詢是否有用到索引,避免建立太多沒被使用的索引。也會觀察查詢選擇性(如某欄位是否夠分散),確保索引是有價值的。 結論 索引是資料庫效能調校的雙刃劍: 讀取性能 與 寫入開銷 必須取得平衡 透過 持續監控、資料分佈分析 和 業務場景理解,才能打造最貼合需求的索引策略 行動項目: 立刻檢查慢查詢日誌,找出無索引的查詢。 使用 EXPLAIN ANALYZE 確認索引是否生效。 訂定週期性索引健康檢查排程。

在 Web 開發中,SQL Injection(SQL 注入) 是最常見且危害極大的資安漏洞之一。它可以讓攻擊者透過竄改 SQL 查詢語句來未授權地讀取、修改、甚至刪除資料庫中的資料。本文將帶你認識 SQL Injection 是什麼、實際攻擊範例,以及開發者該如何有效預防這種攻擊。 SQL Injection 是什麼? SQL Injection 是一種將惡意 SQL 語句插入應用程式輸入欄位中的攻擊方式,目的是操控原本預期的 SQL 查詢邏輯。 舉例來說,假設以下是一段不安全的登入邏輯: # ❌ 極度危險的程式碼 username = input("請輸入帳號:") password = input("請輸入密碼:") query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{password}'" cursor.execute(query) 當攻擊者輸入以下資料時: username: ' OR 1=1 -- password: 任意內容 查詢語句將變成: SELECT * FROM users WHERE username = '' OR 1=1 --' AND password = '任意內容' 由於 -- 是 SQL 的註解符號,後面的內容會被忽略,而 OR 1=1 永遠為真,攻擊者即可繞過密碼驗證成功登入! SQL Injection 的潛在危害 繞過身份驗證:如上例,攻擊者可直接登入任意帳戶。 資料洩漏:SELECT 語句被竄改,可讀取整個資料表。 資料破壞:透過 INSERT/UPDATE/DELETE 修改或刪除資料。 系統控制:某些情況下可執行資料庫指令進而控制伺服器。 如何預防 SQL Injection? 防範 SQL Injection 是開發者的責任,以下是幾種有效的預防方式: 1. 使用參數化查詢(Prepared Statements) 最根本的解法是使用參數化查詢,將使用者輸入與 SQL 語句分離。 以 Python(Django ORM)為例: # ✅ 安全寫法(ORM 自動處理 SQL 注入風險) User.objects.filter(username=username, password=password).exists() 以 Python 搭配 psycopg2 原生查詢為例: # ✅ 安全寫法(使用參數化) cursor.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password)) 2. 使用 ORM(Object-Relational Mapping) ORM 框架(如 Django ORM、SQLAlchemy)天生會處理 SQL Injection 的風險,因為它們自動將參數轉義為安全格式。 3. 輸入驗證與清洗 雖不能取代參數化查詢,但針對表單輸入進行格式驗證(如 email 格式、字數限制)可降低惡意資料混入。 4. 最小權限原則(Least Privilege) 資料庫帳號應只授予應用程式所需的最低權限。避免使用擁有 DROP、GRANT 等高權限的帳號。 5. 使用 Web 應用防火牆(WAF) WAF 可自動攔截常見的 SQL Injection 攻擊模式,作為第二道防線。 如何檢測是否有 SQL Injection? 使用自動化工具(如 sqlmap)對 API 或表單送出測試 payload。 開發階段可使用 lint 工具檢查潛在的不安全 SQL 組合。 確保有完整的測試覆蓋對外輸入的功能。 面試回答 SQL Injection 是一種資安攻擊手法,攻擊者在表單輸入或 URL 中注入惡意 SQL 語句,藉此操控原本的資料庫查詢邏輯,可能造成未授權的資料讀取、修改,甚至整個資料庫被破壞。 舉例來說,若在登入功能中直接把使用者輸入組合成 SQL 字串而未加防護,攻擊者就能透過像 ' OR 1=1 -- 的輸入繞過驗證。 預防方式主要有三個: 使用參數化查詢(Prepared Statements):將輸入值與 SQL 語句分離,是最根本的解法。 使用 ORM 框架:像是 Django ORM、SQLAlchemy 會自動處理參數轉義。 避免直接拼接 SQL,並搭配輸入驗證與資料庫權限控管,從多層面加強安全性。 在我自己的專案中,例如使用 Django 框架時,會透過 ORM 查詢,確保使用者輸入不會直接進入 SQL 字串,並依角色限制資料庫操作權限,確保資安基本面不被忽略。 總結 安全措施 是否必要 備註 參數化查詢 ✅ 必須實作 最根本防禦方式 ORM 框架 ✅ 強烈建議 自動處理轉意 輸入驗證 ⚠️ 輔助防禦 不能單獨依賴 資料庫權限控制 ✅ 應落實 防止資料被大規模破壞 Web 防火牆 ⚠️ 加強防禦 作為額外保護 SQL Injection 是老生常談但仍極常見的安全漏洞,唯有落實安全開發習慣,才能避免這類攻擊發生。 延伸閱讀 OWASP: SQL Injection Django Security Docs

前端開發常常會遇到「跨來源請求被阻擋」的錯誤訊息,比如: Access to fetch at 'https://api.example.com/data' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present... 這就是著名的 CORS 問題。本篇文章將說明 CORS 是什麼、為什麼會發生,以及常見的解法。 CORS 是什麼? CORS(Cross-Origin Resource Sharing,跨來源資源共享)是一種瀏覽器的安全機制,用來限制「不同網域」之間的請求。簡單來說,它防止了一個網站的 JavaScript 去隨意讀取另一個網站的資料。 什麼是「跨來源」? 只要以下任何一個不同,就會被視為「不同來源」: 區塊 範例 協定 http://example.com vs https://example.com 網域 api.example.com vs www.example.com Port example.com:3000 vs example.com:8000 為什麼會出現 CORS 錯誤? 瀏覽器會阻擋從網頁腳本對其他來源發起的請求(例如使用 fetch、axios)。除非 伺服器端主動聲明允許該請求的來源,否則請求會被攔截。 範例情境 假設前端使用 React (http://localhost:3000),想從後端 API (http://localhost:8000) 取得資料: fetch('http://localhost:8000/api/data') 這是跨來源請求,除非後端有設定 CORS header,否則會被瀏覽器擋下來。 解決 CORS 問題的方法 1. 修改後端設定 CORS 問題應由「伺服器端」解決。需要在伺服器回應中加上正確的 Access-Control-Allow-Origin header。 範例:使用 Express const express = require('express'); const cors = require('cors'); const app = express(); app.use(cors()); // 允許所有來源 // 或只允許特定來源 // app.use(cors({ origin: 'http://localhost:3000' })); app.get('/api/data', (req, res) => { res.json({ message: 'Hello from server!' }); }); app.listen(8000); 範例:使用 Django + django-cors-headers pip install django-cors-headers # settings.py INSTALLED_APPS = [ ... 'corsheaders', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', ... ] # 允許所有網域 CORS_ALLOW_ALL_ORIGINS = True # 或只允許特定網域 # CORS_ALLOWED_ORIGINS = [ # "http://localhost:3000", # ] 2. 使用 Proxy 避開 CORS 開發時可使用前端代理轉發請求,例如 React: // vite.config.js or package.json proxy: { '/api': { target: 'http://localhost:8000', changeOrigin: true, }, } 這樣 fetch('/api/data') 實際會被代理成後端的請求,不會被視為跨來源。 3. 後端設定 Access-Control Headers 除了 Access-Control-Allow-Origin,有時也需要加上這些 header: Access-Control-Allow-Origin: http://localhost:3000 Access-Control-Allow-Methods: GET, POST, PUT, DELETE Access-Control-Allow-Headers: Content-Type, Authorization 這些設定也必須在「預檢請求(Preflight Request)」中正確處理。 面試題目 「請說明什麼是 CORS(Cross-Origin Resource Sharing),以及如果遇到 CORS 問題該如何解決?」 回答範例: CORS(Cross-Origin Resource Sharing) 是一種瀏覽器的安全機制,用來限制前端從不同來源(origin)去請求資源,避免惡意網站竊取資料。當前端的 JavaScript 嘗試向不同網域、協定或 port 發送請求時,如果伺服器沒有正確設定 CORS header,瀏覽器會阻擋這個請求。 常見錯誤: 例如使用 fetch 或 axios 向不同來源的 API 發送請求時,會看到類似錯誤: Access to fetch at 'http://api.example.com' from origin 'http://localhost:3000' has been blocked by CORS policy 解決方式: 從伺服器端設定 CORS header(這是正確的做法): 設定 Access-Control-Allow-Origin,明確允許某個前端網域。 可搭配套件,如: Node.js 可用 cors middleware Django 可用 django-cors-headers 本地開發時使用 proxy 轉發請求,避開跨域問題(開發用): 例如 React 可以設定 vite.config.js 或 webpack devServer proxy 總結 問題 解法 跨來源請求被阻擋 在後端加上 CORS Header 特定網域允許 設定 Access-Control-Allow-Origin 為指定來源 多來源需求 使用動態設定或 whitelist 本地開發測試 使用 Proxy 或開啟允許全部來源 延伸閱讀 MDN - HTTP access control (CORS) CORS 官方規範 (W3C) django-cors-headers GitHub 如果曾經在開發中遇過 CORS 錯誤,不妨回頭看看是不是跨來源造成的限制,並從伺服器端著手解決吧!

使用 Supabase 替代本機安裝的 PostgreSQL 並連接至 Django 在開發應用程式時,許多開發者會選擇在本機安裝 PostgreSQL 資料庫。然而,隨著雲端技術的普及,使用遠端資料庫如 Supabase 提供的 PostgreSQL 不僅可以減少本機配置的麻煩,也能讓資料庫更容易在團隊間共享。 以下是完整的指南,教導如何取代本機安裝的 PostgreSQL,並利用 Django 連接至 Supabase 資料庫。 1. 在 Supabase 官網建立資料庫 前往 Supabase 官網 並註冊帳號。 登入後,點擊 “New Project” 按鈕建立新的專案。 填寫相關資訊: Project name: 為專案命名。 Database password: 設定資料庫的密碼(請妥善保存)。 Region: 選擇靠近的伺服器區域以減少延遲。 點擊 “Create New Project”,Supabase 將自動配置資料庫,這過程大約需 2-3 分鐘。 完成後,可以在專案的 Settings 頁面下找到資料庫的連線資訊,包括 Host、Port、Database name 和 User credentials。 2. 安裝 Django 和必要的套件 如果尚未安裝 Django,請在專案環境中安裝 Django 以及支援 PostgreSQL 的套件: uv add "psycopg[binary]" 3. 配置 Django 專案連接 Supabase 在 Django 專案中,修改 settings.py 文件,將資料庫設定更改為 Supabase 提供的連線資訊: import os from pathlib import Path import environ # env init env = environ.Env() # 預設會讀取專案根目錄的 `.env` environ.Env.read_env(os.path.join(BASE_DIR, '.env')) DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': env('DB_NAME'), 'USER': env('DB_USER'), 'PASSWORD': env('DB_PASSWORD'), 'HOST': env('DB_HOST'), 'PORT': env('DB_PORT'), } } 在專案根目錄建立一個 .env 檔案,並填入以下設定(請將值替換為 Supabase 提供的實際資訊): DB_NAME=your_database_name DB_USER=postgres DB_PASSWORD=your_database_password DB_HOST=db.xxxxxxxxxxxx.supabase.co DB_PORT=5432 請注意: 確保 .env 檔案已加入到 .gitignore 中,避免敏感資訊被上傳到版本控制系統。 資料庫使用者名稱通常是 postgres,這是 Supabase 的預設設定。 資料庫密碼是建立專案時設定的密碼。 主機名稱和連接埠可以在 Supabase 專案的設定頁面中找到。 4. 測試連線 執行以下指令以測試資料庫是否正確連接: uv run python manage.py migrate 如果配置無誤,會看到 Django 成功執行資料庫遷移。 5. 小結 至此,完成將本機 PostgreSQL 替換為 Supabase 遠端資料庫,並成功使用 Django 進行連接。不僅減少了本機安裝的負擔,還能利用 Supabase 提供的強大功能,如即時同步和 API 支援,提升開發效率。

本文詳細解釋了 Git 本機與遠端操作的常見指令及其應用場景,涵蓋 git fetch、git pull、git push、Pull Request (PR) 的管理,以及如何保持 Git 圖表清晰的最佳實踐。 git fetch 與分支同步 git fetch 是用來從遠端倉庫抓取最新的更新,但不會自動合併到本地分支。它的作用是讓本地倉庫知道遠端的最新狀態。 使用範例: git fetch 效果:抓取遠端的更新,並建立對應的遠端分支(如 origin/master)。 應用場景:當你想檢查遠端的最新變更,但不希望立即影響本地分支時使用。 本機與遠端同步的方式 要讓本機分支跟上遠端的進度,可以使用以下幾種方式: 1. git merge 將遠端的變更合併到本地分支,保留本地和遠端的提交歷史。 git pull = git fetch + git merge 效果:合併後會保留兩條提交記錄,適合需要完整歷史的場景。 缺點:可能導致 Git 圖表變得複雜。 2. git rebase 將遠端的變更應用到本地分支的基礎上,重新整理提交歷史。 git pull --rebase = git fetch + git rebase 效果:提交歷史會變得線性,讓 Git 圖表更清晰。 注意:在使用 rebase 時,需確保沒有其他人依賴你的分支,避免衝突。 3. git reset 重置本地分支到遠端分支的狀態,適合需要完全同步的情況。 git push 的進階用法 git push 是將本地分支的變更推送到遠端分支。以下是一些進階用法: 1. 推送到相同名稱的分支 git push origin main 效果:將本地的 main 分支推送到遠端的 main 分支。 2. 推送到不同名稱的分支 git push origin main:cat 效果:將本地的 main 分支推送到遠端的 cat 分支。 3. 刪除遠端分支 git push origin :cat 效果:刪除遠端的 cat 分支,相當於執行 null:cat。 Pull Request (PR) 的管理 Pull Request 是團隊協作中不可或缺的一部分,用於代碼審查和合併。 PR 的最佳實踐 設定審核條件: 可以設定 6 人團隊中至少 5 人接受後才能合併。 當條件滿足時,合併按鈕會亮起,任何人都可以按下合併。 PR 的重要性: 即使功能不是你開發的,參與 PR 的 Code Review 也能幫助你了解功能的運作方式。 在面試中,可以提到你參與過的 Code Review,展示對系統的理解。 避免 PR 堆積: PR 長時間未處理會導致難以合併,建議每週安排一次 PR 日,集中討論和處理 PR。 保持 Git 圖表清晰的建議 優先使用 git rebase: 在執行 git fetch 後,盡量使用 rebase 而非 merge,避免提交歷史過於複雜。 避免強制推送到共享分支: git push origin main --force 應謹慎使用,尤其是在共享分支上。 如果需要整理自己的分支,可以在個人分支上使用 --force。 常見操作範例 建立新分支並推送 git branch issues/705 git push origin issues/705 效果:建立一個名為 issues/705 的分支,並推送到遠端。 發起 PR 並進行 Code Review 在遠端倉庫中發起 PR,請求其他成員進行代碼審查。 通過審查後,合併到主分支。 面試中的 Git 技巧 在面試中,展示你對 Git 的理解和實踐經驗非常重要。例如: 參與 Code Review: 即使某功能不是你開發的,可以提到在 PR 中進行過代碼審查,並了解該功能的實現細節。 版本控制的實踐: 提到如何使用 rebase 保持提交歷史清晰,或如何處理衝突。 這篇文章介紹了 Git 本機與遠端操作的常見場景和實踐,適合用於團隊協作和個人開發的日常工作中。

這篇文章描述了任務管理與執行流程,涵蓋點數評估、任務拆解、立會流程、PR 管理以及團隊協作的實踐,適合用於團隊內部的工作指導與知識共享。 點數評估與難易程度 在專案管理中,為了合理分配任務並估算完成時間,點數評估是一個重要的工具。點數的大小通常與任務的難易程度和所需時間成正比。 點數對應時間: 半天:1 點 一天:2 點 兩天半:5 點 這樣的點數設計可以幫助團隊快速了解任務的規模,並根據每位成員的能力合理分配工作。小任務(1 點)適合快速完成,而大任務(5 點)則需要進一步拆解或由多位成員協作完成。 任務拆解與點數決定 點數如何決定? 點數的評估並不是由單一個人決定,而是由所有組員共同討論完成的。這樣可以避免因個人偏見導致的評估不準確,並且能夠考慮到每位組員的能力值和經驗。 任務拆解的原則 將大任務拆解為小任務: 確保每個任務的點數不超過 5 點,這樣可以降低任務的複雜度,讓進度更容易掌控。 優先處理高優先級任務: 如果某些任務會阻塞其他工作,應該優先完成,確保整體進度不受影響。 任務拆解的目的是讓每個人都能清楚知道自己該做什麼,並且能夠在合理的時間內完成。 路障型任務處理 在專案中,總會遇到一些「路障型任務」,這些任務可能因為技術難題或外部依賴而無法順利進行。為了避免這些任務拖延整體進度,我們採取以下策略: 誰先舉手就做: 當遇到路障型任務時,鼓勵組員主動承擔,但需要設定明確的停損期。 設定停損期: 如果在停損期內無法解決問題,應立即向組員求助,或將任務轉交給其他人處理。 這樣的處理方式可以有效避免任務長時間卡住,確保專案能夠持續推進。 立會流程 每日立會是敏捷開發中的一個重要環節,目的是讓團隊成員同步進度,及時解決問題。 每日立會內容 昨天做了什麼? 回顧昨天的工作,確認是否完成預定的任務。 遇到什麼困難? 如果有卡住的問題,應該在立會中提出,尋求團隊的幫助。 預計今天要做什麼? 明確當天的工作計劃,確保每個人都有清晰的目標。 確認票的狀態 如果有票可能卡住,應該立即求救,或撿其他票來做,避免進度延誤。 目標是確保所有票都能按時完成,避免影響整體進度。 任務優先級與進度追蹤 優先級設定 在專案中,並非所有任務的優先級都是相同的。我們可以根據任務的重要性和緊急程度進行分類: 高優先級:影響整體進度或其他任務的工作,應該優先完成。 中優先級:需要在一定時間內完成,但不會阻塞其他任務。 低優先級:可以延後處理的任務。 進度追蹤工具 為了更好地追蹤任務進度,可以使用看板工具(如 Trello、Jira)。將任務狀態分為以下幾類: 待處理:尚未開始的任務。 進行中:正在處理的任務。 已完成:已完成的任務。 阻塞:因外部原因無法繼續的任務。 這樣的分類可以幫助團隊快速了解專案的整體進度。 任務完成的驗收標準 驗收標準 任務是否達到預期的功能或目標。 是否通過測試(單元測試、整合測試等)。 是否符合代碼規範(如代碼審查通過)。 驗收流程 開發者完成任務後,提交 Pull Request(PR)。 組員進行 Code Review,確保代碼質量。 通過測試後,合併到主分支。 驗收標準的制定可以確保每個任務都能以高質量完成,並減少後續的返工。 Pull Request(PR)管理 PR 的重要性 Pull Request 是團隊協作中不可或缺的一部分。它不僅可以確保代碼質量,還能促進團隊成員之間的知識共享。 PR 的最佳實踐 小而頻繁的 PR: 避免一次提交過多代碼,讓審查更高效。 詳細描述修改內容: 在 PR 中清楚說明修改的目的和內容,方便其他人理解。 定期清理過期 PR: 避免堆積過多未處理的 PR,影響進度。 PR 的合併條件 可以設定需要一定人數的審核通過(如 5 人團隊中至少 3 人同意)。 確保所有測試通過後才能合併。 團隊協作的注意事項 溝通與協作 定期同步進度,確保所有人了解當前狀態。 鼓勵組員主動提出問題或建議,促進團隊合作。 知識共享 透過 Code Review 和立會,促進團隊對系統的共同理解。 使用文檔記錄重要的技術決策和流程,方便後續查閱。 常見問題與解決方案 票卡住了怎麼辦? 立即向組員求助,尋求解決方案。 如果短時間內無法解決,轉交給其他人處理,或重新評估任務。 如何避免票交不出來? 設定合理的停損期。 定期檢查任務進度,及早發現問題。 如何處理路障型任務? 設定明確的停損期,避免浪費過多時間。 將問題記錄下來,作為後續優化的參考。

什麼是 N+1 問題? N+1 問題 是一種常見的效能問題,通常發生在處理資料庫查詢時。當我們需要查詢一個主物件及其相關聯的子物件時,程式會執行 1 次查詢來獲取主物件,然後為每個主物件執行 N 次查詢來獲取其相關聯的子物件。這樣總共會執行 1 + N 次查詢,導致效能低下。 什麼時候會發生 N+1 問題? 關聯查詢: 當模型之間有外鍵(ForeignKey)、一對一(OneToOneField)或多對多(ManyToManyField)關聯時,容易發生 N+1 問題。 模板中迴圈訪問關聯物件: 在模板中使用迴圈訪問關聯物件時,可能會導致多次查詢。 範例: {% for comment in comments %} {{ comment.author.name }} {% endfor %} 範例:留言與作者 (多對一) 假設我們有一個留言系統,每條留言都有一個作者。當我們需要顯示所有留言及其作者時,可能會發生 N+1 問題。 查詢留言:執行 1 次查詢來獲取所有留言。 SELECT * FROM comments; 查詢作者:對於每條留言,執行 1 次查詢來獲取該留言的作者。 SELECT * FROM users WHERE id = <author_id>; 如果有 100 條留言,總共會執行 1 + 100 = 101 次查詢。 N+1 問題的影響 效能低下:大量的查詢會增加資料庫的負載,特別是在高流量的應用中。 延遲增加:每次查詢都需要與資料庫交互,導致頁面加載時間變長。 解決方法 1. 使用 select_related select_related 用於解決 一對一 或 多對一 關聯的 N+1 問題。它會在一次查詢中使用 SQL 的 JOIN,將相關聯的資料一起查出來。 範例 (多對一): # N+1 問題 comments = Comment.objects.all() for comment in comments: print(comment.author.name) # 每次訪問 author 都會執行一次查詢 # 解決 N+1 問題 comments = Comment.objects.select_related('author') for comment in comments: print(comment.author.name) # 不會再執行額外的查詢 使用 select_related,Django 在查詢留言時,會同時透過 SQL 的 JOIN 將留言和作者的資料一起查出來。 SQL 查詢: SELECT comments.*, users.* FROM comments JOIN users ON comments.author_id = users.id; SQL 查詢對比 方法 查詢次數 SQL 查詢範例 N+1 問題 1 + N 1. SELECT * FROM comments; 2. SELECT * FROM users WHERE id = <id>; 使用 select_related 1 SELECT comments.*, users.* FROM comments JOIN users ON comments.author_id = users.id; 2. 使用 prefetch_related prefetch_related 用於解決 一對多 或 多對多 關聯的 N+1 問題。它會執行兩次查詢,並在 Python 中將結果關聯起來。 範例 (一對多): # N+1 問題 authors = Author.objects.all() for author in authors: print(author.books.all()) # 每次訪問 books 都會執行一次查詢 # 解決 N+1 問題 authors = Author.objects.prefetch_related('books') for author in authors: print(author.books.all()) # 不會再執行額外的查詢 prefetch_related 會執行兩次查詢: 查詢所有作者。 查詢所有書籍,並在 Python 中將書籍與對應的作者關聯起來。 SQL 查詢: SELECT * FROM authors; SELECT * FROM books WHERE author_id IN (<author_ids>); SQL 查詢對比 方法 查詢次數 SQL 查詢範例 N+1 問題 1 + N 1. SELECT * FROM authors; 2. SELECT * FROM books WHERE author_id = <author_id>; prefetch_related 2 1. SELECT * FROM authors; 2. SELECT * FROM books WHERE author_id IN (<author_ids>); N+1 問題的查詢(使用 =) SELECT * FROM books WHERE author_id = 1; SELECT * FROM books WHERE author_id = 2; SELECT * FROM books WHERE author_id = 3; 每次查詢只匹配一個 author_id,需要執行多次查詢(N 次)。 prefetch_related 的查詢(使用 IN) SELECT * FROM books WHERE author_id IN (1, 2, 3); 一次查詢即可匹配多個 author_id,大幅減少查詢次數。 在解決 N+1 問題時,prefetch_related 使用 IN,可以一次查詢多個關聯物件,顯著提升效能 select_related 與 prefetch_related 的比較 select_related 和 prefetch_related 是 Django 中用來解決 N+1 問題的兩個方法,但它們適用於不同的場景,且工作方式也不同 特性 select_related prefetch_related 適用關係 多對一、一對一 一對多、多對多 查詢次數 1 次查詢 2 次查詢 工作方式 使用 SQL 的 JOIN 分開查詢,Python 中關聯結果 效能 高效,但不適合關聯資料過多的情況 稍低,但適合處理大量關聯資料 使用場景 外鍵(ForeignKey)、一對一關聯 反向外鍵(related_name)、多對多關聯 根據資料關聯的類型選擇正確的方法,可以有效提升 Django 應用的效能 在某些情況下,兩者都可以使用,但選擇哪一個取決於以下因素: 資料量大小: 如果關聯資料量較小,使用 select_related 更高效,因為只需要執行一次查詢。 如果關聯資料量較大,使用 prefetch_related 更合適,因為它避免了 JOIN 導致的查詢結果過於龐大。 關聯類型: 多對一或一對一:優先使用 select_related。 一對多或多對多:優先使用 prefetch_related。 查詢的靈活性: 如果需要對關聯表進行額外的過濾或排序,使用 prefetch_related 更靈活,因為它允許自訂查詢。 select_related 無法對關聯表進行過濾或排序,因為它直接使用 SQL 的 JOIN 當關聯表的資料量較大,且需要過濾或排序時,prefetch_related 是更好的選擇 3. 使用 Django Debug Toolbar Django Debug Toolbar 是一個強大的工具,可以幫助開發者檢測 N+1 問題,並查看每個請求執行的 SQL 查詢。 安裝與設定: 安裝: pip install django-debug-toolbar 在 settings.py 中添加: INSTALLED_APPS += ['debug_toolbar'] MIDDLEWARE += ['debug_toolbar.middleware.DebugToolbarMiddleware'] INTERNAL_IPS = [ # ... "127.0.0.1", # ... ] 在 urls.py 中添加: from django.conf import settings from django.conf.urls import include from django.urls import path if settings.DEBUG: import debug_toolbar urlpatterns = [ path('__debug__/', include(debug_toolbar.urls)), ] + urlpatterns 啟動伺服器後,檢查每個請求的 SQL 查詢次數,找出 N+1 問題。 總結 N+1 問題 是由於多次查詢資料庫導致的效能問題,特別是在處理關聯資料時容易發生。 解決方法: 使用 select_related 解決一對一或多對一關聯的問題。 使用 prefetch_related 解決一對多或多對多關聯的問題。 使用 Django Debug Toolbar 檢測 SQL 查詢次數,找出潛在的 N+1 問題。 最佳實踐: 在開發過程中,定期檢查 SQL 查詢,確保效能最佳化。 對於大型應用,考慮使用快取(如 Redis)進一步減少資料庫查詢次數。 透過這些方法,可以有效避免 N+1 問題,提升 Django 應用的效能與穩定性。

什麼是 Django Messages? Django 提供了一個內建的訊息框架(django.contrib.messages),用於在請求之間傳遞提示訊息,這些訊息通常用於通知使用者某些操作的結果,例如成功、錯誤或警告等。這類訊息被稱為 「快閃訊息」(Flash Messages),因為它們只會在下一次請求中顯示,然後自動消失。 使用場景 Django Messages 適合用於以下場景: 表單提交成功或失敗的提示。 操作完成後的通知(例如:資料已儲存、刪除成功)。 錯誤或警告訊息的顯示。 快閃訊息的類型 Django 提供了以下內建的訊息類型: messages.debug: 用於調試訊息。 messages.info: 一般資訊提示。 messages.success: 成功訊息。 messages.warning: 警告訊息。 messages.error: 錯誤訊息。 如何使用 Django Messages? 1. 啟用 Messages 確保 django.contrib.messages 已包含在 INSTALLED_APPS 中,並且模板中已載入 messages 中間件。 settings.py: INSTALLED_APPS = [ ... 'django.contrib.messages', ... ] MIDDLEWARE = [ ... 'django.contrib.messages.middleware.MessageMiddleware', ... ] 2. 在視圖中添加訊息 使用 messages 模組在視圖中添加訊息。 範例: from django.contrib import messages from django.shortcuts import redirect def my_view(request): # 添加成功訊息 messages.success(request, "操作成功!") # 添加警告訊息 messages.warning(request, "這是警告訊息!") return redirect("home") 3. 在模板中顯示訊息 在模板中使用 messages 模板標籤來顯示訊息。 範例: {% if messages %} {% for message in messages %} {{ message }} {% endfor %} {% endif %} message.tags: 自動添加的 CSS 樣式類型(如 success、warning)。 message: 訊息的內容。 4. 自訂樣式 可以根據訊息類型自訂樣式,提升用戶體驗。 範例(CSS): .messages { list-style: none; padding: 0; } .messages .success { color: green; } .messages .warning { color: orange; } .messages .error { color: red; } 完整範例 以下是使用 DaisyUI 和 Alpine.js 實現的完整範例,包含可點擊 x 按鈕關閉訊息的功能,並根據訊息類型(如 success、warning、error)自動套用樣式: {% if messages %} {% for message in messages %} {{ message }} x {% endfor %} {% endif %} 功能說明 Alpine.js 的 x-data 和 x-if: 使用 x-data="{ show: true }" 初始化訊息的顯示狀態。 使用 x-if="show" 控制訊息是否顯示,點擊 x 按鈕後將 show 設為 false,隱藏訊息。 DaisyUI 的 alert 樣式: 使用 alert 類別來套用 DaisyUI 的樣式。 根據 message.tags 動態添加樣式類別(如 alert-success、alert-warning、alert-error 等)。 s 關閉按鈕: 使用 DaisyUI 的按鈕樣式 btn btn-xs btn-circle btn-outline。 點擊按鈕時,透過 Alpine.js 的 @click 事件隱藏訊息。 Alpine.js 和 DaisyUI 的引入 Alpine.js 的引入: 確保在模板中已經引入 Alpine.js CDN: <script src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js" defer></script> DaisyUI 的安裝: DaisyUI 是基於 Tailwind CSS 的元件庫,確保已正確安裝並配置 Tailwind 和 DaisyUI。 這樣的設計不僅美觀,還能提供良好的用戶體驗,適合用於各種 Django 項目中需要提示訊息的場景。 注意事項 訊息的持久性: Django Messages 預設只會在下一次請求中顯示,之後會自動清除。 如果需要更長時間的訊息顯示,可以考慮自訂訊息存儲(Message Storage)。 訊息存儲後端: 預設使用 CookieStorage 或 SessionStorage。 可以在 settings.py 中自訂:MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage' 多條訊息顯示: 如果在同一個請求中添加多條訊息,messages 會以列表的形式顯示。 總結 Django Messages 是一個簡單而強大的工具,適合用於提示訊息的顯示。通過內建的訊息類型和模板標籤,可以快速實現用戶友好的通知功能,並提升應用的交互體驗。

功能概述 此功能實現了一個按讚(收藏)按鈕,使用者可以對特定的項目(如面試記錄)進行按讚或取消按讚操作。按鈕的狀態(已收藏或收藏)由後端決定,並通過 HTMX 局部更新頁面。 技術細節 1. 前端模板設計 favorite.html 這個模板負責渲染按讚按鈕,按鈕的狀態根據使用者是否已經按讚來決定。 {% if user in interview.favorited_by.all %} 已收藏 {% else %} 收藏 {% endif %} hx-post: 當按鈕被點擊時,發送一個 POST 請求到指定的 URL。 hx-swap="outerHTML": 指定 HTMX 替換整個按鈕的 HTML,而不僅僅是按鈕內的內容。 按鈕狀態: 使用 Django 模板語法 `
0%