在開發過程中,我們經常會遇到一種「似曾相識」的時刻。明明是熟悉的函數,但看到 import 路徑時,才發現它並非我們所認識的那個。對於 Django 開發者而言,urlencode 就是這樣一個函數。

對 Django 有一定經驗的開發者都知道 urlencode 位於 django.utils.http,但初學者往往會習慣性地使用 Python 標準函式庫中著名的 urllib.parse.urlencode。(我以前也曾如此。)

這個同時存在於 Python 標準函式庫和 Django 工具集中的函數,真的可以隨意選用嗎?答案是:「不行」。這兩個函數之間存在著微妙卻關鍵的差異,本文將為您詳細整理。

a dev is being confused about choosing a method


名稱相同但結果不同?Django 開發中使用 urlencode 時的常見錯誤

Python 的 urllib.parse.urlencode 和 Django 的 django.utils.http.urlencode 儘管名稱和目的看似相同,但 Django 版本是為了應對網頁開發的特殊情境而更進一步演化的形式。

1. 核心差異一覽

這兩個函數的主要差異總結如下表:

區分 urllib.parse.urlencode django.utils.http.urlencode
所屬 Python 標準函式庫 Django 內建工具
實作方式 標準函式庫獨立實作 內部擴展呼叫 urllib 版本
基本行為 將字典轉換為查詢字串 針對 QueryDict 及多值處理進行優化
列表處理 必須使用 doseq=True 選項 無須額外選項也能安全處理列表

2. 為何要獨立使用 Django 版本?(最重要的原因)

Django 之所以不直接使用標準函式庫,而特地建立自己的 urlencode,有其明確的原因。這主要是為了在 網頁環境中提供更高的穩定性便利性

第一,列表(多值)處理的「安全機制」

在網頁協定中,一個鍵對應多個值的情況很常見(例如:?tag=python&tag=django)。Python 標準 urllib 版本在編碼列表時,必須手動帶上 doseq=True 選項。如果忘記了,列表物件本身會被轉換成字串,產生像 tag=['python', 'django'] 這樣錯誤的結果。

相對地,Django 版本則是在「網頁中預設將列表展開傳送」的前提下設計,因此無須額外選項,也能完美地編碼列表資料。

第二,與 QueryDict 的完美兼容性

Django 的 request.GET 並非一般的字典,而是一個 QueryDict 物件。QueryDict 是一種特殊物件,允許同一個鍵擁有多個值。Django 的 urlencode 能夠精確理解這個物件的特性,並利用其內部方法,確保資料轉換時不遺漏任何資訊。


3. 運用 QueryDict 的實戰編碼案例

接下來,我們將探討 Django 的 urlencode 在實際專案中發揮作用的兩個情境。

案例 1:在分頁時保留搜尋篩選條件

當使用者需要維持已選擇的多個搜尋條件,同時僅移動頁面時,直接將 request.GET 完整編碼是最簡潔的做法。

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:傳送多選核取方塊資料

當需要將多個核取方塊資料以字典形式編碼,並傳遞給其他 API 或頁面時,這項功能非常實用。

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. 總結:該如何選擇?

選擇的標準非常明確。

  1. 如果在 Django 專案內部處理 request.GET 或生成網頁 URL?

    • 毫不猶豫地使用 django.utils.http.urlencode
  2. 如果是編寫 與 Django 無關的獨立 Python 腳本

    • 使用 urllib.parse.urlencode,但若有列表資料,請務必記得加上 doseq=True

歸根結底,Django 版本是個「友善的包裝器(Wrapper)」,它不僅包含了標準函式庫的所有功能,還能減少開發者犯錯的機會。別忘了,這些微小的差異能為您省下大量的除錯時間!