在開發過程中,我們經常會遇到一種「似曾相識」的時刻。明明是熟悉的函數,但看到 `import` 路徑時,才發現它並非我們所認識的那個。對於 Django 開發者而言,`urlencode` 就是這樣一個函數。 對 Django 有一定經驗的開發者都知道 `urlencode` 位於 `django.utils.http`,但初學者往往會習慣性地使用 Python 標準函式庫中著名的 `urllib.parse.urlencode`。(我以前也曾如此。) 這個同時存在於 Python 標準函式庫和 Django 工具集中的函數,真的可以隨意選用嗎?答案是:**「不行」**。這兩個函數之間存在著微妙卻關鍵的差異,本文將為您詳細整理。 ![a dev is being confused about choosing a method](/media/whitedec/blog_img/9215d69669ef4f36b9be389a5594ba1f.webp) --- # 名稱相同但結果不同?Django 開發中使用 urlencode 時的常見錯誤 Python 的 `urllib.parse.urlencode` 和 Django 的 `django.utils.http.urlencode` 儘管名稱和目的看似相同,但 Django 版本是為了應對網頁開發的特殊情境而更進一步演化的形式。 ## 1. 核心差異一覽 {#sec-e4fba724ad11} 這兩個函數的主要差異總結如下表: |**區分**|**urllib.parse.urlencode**|**django.utils.http.urlencode**| |---|---|---| |**所屬**|Python 標準函式庫|Django 內建工具| |**實作方式**|標準函式庫獨立實作|內部擴展呼叫 `urllib` 版本| |**基本行為**|將字典轉換為查詢字串|**針對 `QueryDict` 及多值處理進行優化**| |**列表處理**|必須使用 `doseq=True` 選項|**無須額外選項也能安全處理列表**| --- ## 2. 為何要獨立使用 Django 版本?(最重要的原因) {#sec-4eb8047c69f0} Django 之所以不直接使用標準函式庫,而特地建立自己的 `urlencode`,有其明確的原因。這主要是為了在 **網頁環境中提供更高的穩定性** 和 **便利性**。 ### 第一,列表(多值)處理的「安全機制」 {#sec-0927ec9ca7b1} 在網頁協定中,一個鍵對應多個值的情況很常見(例如:`?tag=python&tag=django`)。Python 標準 `urllib` 版本在編碼列表時,必須手動帶上 `doseq=True` 選項。如果忘記了,列表物件本身會被轉換成字串,產生像 `tag=['python', 'django']` 這樣錯誤的結果。 相對地,Django 版本則是在「網頁中預設將列表展開傳送」的前提下設計,因此無須額外選項,也能完美地編碼列表資料。 ### 第二,與 `QueryDict` 的完美兼容性 {#sec-fc334b880462} Django 的 `request.GET` 並非一般的字典,而是一個 `QueryDict` 物件。`QueryDict` 是一種特殊物件,允許同一個鍵擁有多個值。Django 的 `urlencode` 能夠精確理解這個物件的特性,並利用其內部方法,確保資料轉換時不遺漏任何資訊。 --- ## 3. 運用 QueryDict 的實戰編碼案例 {#sec-d12e4e553206} 接下來,我們將探討 Django 的 `urlencode` 在實際專案中發揮作用的兩個情境。 ### 案例 1:在分頁時保留搜尋篩選條件 {#sec-6144c74d2640} 當使用者需要維持已選擇的多個搜尋條件,同時僅移動頁面時,直接將 `request.GET` 完整編碼是最簡潔的做法。 ```Python from django.utils.http import urlencode # 使用者 ?category=tech&category=life&q=django 的搜尋情境 def get_next_page_url(request): params = request.GET.copy() # 複製 QueryDict params['page'] = 2 # 只更新頁碼 # Django 的 urlencode 會自動處理 QueryDict 的多值 (兩個 category) return f"/search/?{urlencode(params)}" # 結果: /search/?category=tech&category=life&q=django&page=2 ``` ### 案例 2:傳送多選核取方塊資料 {#sec-5e6185fe793c} 當需要將多個核取方塊資料以字典形式編碼,並傳遞給其他 API 或頁面時,這項功能非常實用。 ```Python from django.utils.http import urlencode data = { 'user_id': 123, 'selected_tags': ['python', 'backend', 'tips'] } # 與標準 urllib 不同,無須使用 doseq=True query_string = urlencode(data) print(query_string) # 結果: user_id=123&selected_tags=python&selected_tags=backend&selected_tags=tips ``` --- ## 4. 總結:該如何選擇? {#sec-eb673ad941be} 選擇的標準非常明確。 1. 如果在 **Django 專案內部**處理 `request.GET` 或生成網頁 URL? - 毫不猶豫地使用 **`django.utils.http.urlencode`**。 2. 如果是編寫 **與 Django 無關的獨立 Python 腳本**? - 使用 **`urllib.parse.urlencode`**,但若有列表資料,請務必記得加上 `doseq=True`。 歸根結底,Django 版本是個「友善的包裝器(Wrapper)」,它不僅包含了標準函式庫的所有功能,還能減少開發者犯錯的機會。別忘了,這些微小的差異能為您省下大量的除錯時間!