新手指南|Django 標籤功能|後端資料儲存邏輯
更新日期: 2025 年 2 月 5 日
本文為 Django 標籤功能 系列文,第 4 篇
- 新手指南:在 Django 中實作標籤功能
- Django 新手指南:django-taggit 簡介
- 新手指南:使用 Alpine.js 實現前端標籤輸入功能
- 新手指南|Django 標籤功能|後端資料儲存邏輯 👈所在位置
- 新手指南:Django 標籤顯示功能解析
- Djano 標籤編輯頁|前、後端設定說明
- 使用 Django 建立標籤分類頁:新手教學
在 Django 中,為模型設置標籤是一個常見的需求,例如為服務添加主題標籤、為服務分類標籤等。
當涉及表單提交與多對多關係保存時,可能會讓新手感到困惑,特別是處理標籤數據的邏輯。
本文將通過詳細解說範例代碼,幫助新手理解如何設計表單、編寫後端處理函數,以及為什麼需要特定的保存步驟。
表單設計:允許用戶輸入標籤
首先,我們需要定義一個表單,讓用戶可以通過表單提交標籤和其他數據。
在範例中,我們的 ArticleForm
允許用戶輸入服務的標題、描述、分類、照片和標籤。
表單定義
以下是範例代碼:
from django import forms
from .models import Article
class ServiceForm(forms.ModelForm):
class Meta:
model = Service
fields = ['title', 'description', 'category', 'photo', 'tags']
關於 tags
欄位
tags
是一個多對多字段(ManyToManyField),允許為服務添加多個標籤。- 用戶可以輸入逗號分隔的標籤(例如:
Django, Python, Web Development
),這些標籤會被解析並存儲到資料庫中。
後端函數處理:保存表單數據與標籤邏輯
在後端,我們需要處理表單提交,並正確保存標籤與多對多關係。
範例代碼解析
以下是保存數據的核心函數:
@login_required
def create_service(request, id):
if not has_permission(request, id):
return redirect("services:error_page")
categories = Category.objects.all() # 獲取所有分類
if request.method == "POST":
form = ServiceForm(request.POST, request.FILES)
if form.is_valid():
# 1. 使用 commit=False 保存主對象,但不立即寫入資料庫
service = form.save(commit=False)
service.freelancer_user = request.user # 設置額外欄位
service.save() # 保存到資料庫,獲取主對象的 ID
# 2. 處理多對多字段(分類與標籤)
selected_categories = request.POST.getlist("category")
service.category.set(selected_categories) # 設置分類
tags_input = request.POST.get("tags", "")
if tags_input:
service.tags.set(
[tag.strip() for tag in tags_input.split(",") if tag.strip()]
) # 分割並清理標籤
else:
service.tags.clear() # 如果沒有輸入標籤,清空
# 3. 保存多對多關係
form.save_m2m()
return redirect("services:freelancer_dashboard", id=id)
else:
form = ServiceForm()
return render(
request,
"services/create_service.html",
{
"form": form,
"categories": categories,
"show_loading": True,
},
)
解析:標籤相關代碼詳解
form.save(commit=False)
service = form.save(commit=False)
- 功能:創建一個
service
對象,但不立即保存到資料庫。 - 為什麼需要這步?
- 在保存之前,我們可能需要設置一些額外的字段(例如:
freelancer_user
)。 - 如果直接保存(
form.save()
),這些字段的值無法及時設置。
- 在保存之前,我們可能需要設置一些額外的字段(例如:
service.save()
service.save()
- 功能:將
service
對象保存到資料庫,並生成一個主鍵 ID。 - 必要性:
- 多對多關係(如標籤與分類)需要主對象的 ID 才能正確建立關聯。
設置標籤數據
tags_input = request.POST.get("tags", "")
if tags_input:
service.tags.set(
[tag.strip() for tag in tags_input.split(",") if tag.strip()]
)
else:
service.tags.clear()
- 解析:
- 從用戶輸入中獲取標籤字符串(
tags_input
),例如:Python, Django, Web
. - 使用逗號分割標籤字符串,並清理空格(
tag.strip()
)。 - 調用
set()
方法更新標籤:- 如果有輸入,將解析後的標籤設置到
service.tags
。 - 如果沒有輸入,清空標籤。
- 如果有輸入,將解析後的標籤設置到
- 從用戶輸入中獲取標籤字符串(
- 注意:
set()
方法會自動處理標籤的新增與刪除,避免重複儲存。
form.save_m2m()
form.save_m2m()
- 功能:保存表單中的多對多關係。
- 必要性:
- 當我們使用
commit=False
時,多對多關係不會自動保存。 form.save_m2m()
將確保所有多對多數據(如標籤與分類)寫入資料庫。
- 當我們使用
標籤處理核心邏輯
當我們處理用戶輸入的標籤時,會用到這段代碼:
[tag.strip() for tag in tags_input.split(",") if tag.strip()]
這是一個 串列推導式(List Comprehension),用於從逗號分隔的標籤字符串中提取乾淨的標籤。以下是對此代碼的詳細解釋。
背景
假設用戶通過表單輸入標籤字符串,例如:
tags_input = " Python , Django, , Web Development , "
這是一個逗號分隔的字符串,可能包含:
- 前後的多餘空格(如
" Python "
)。 - 空的標籤(如
", , "
)。
目標是:
- 去掉每個標籤的空格(如把
" Python "
轉為"Python"
)。 - 丟棄空的標籤(如忽略
""
)。
拆解代碼
分割字符串
tags_input.split(",")
- 使用逗號
,
作為分隔符,將字符串拆分為列表。 - 假設
tags_input = " Python , Django, , Web Development , "
,拆分後的結果是:
tags_input = " Python , Django, , Web Development , "
去除空格
tag.strip()
- 對於每個標籤,
strip()
方法用於去掉前後的空白字符。 - 例如:
" Python ".strip() # 結果是 "Python"
" Web Development ".strip() # 結果是 "Web Development"
" ".strip() # 結果是 ""
忽略空標籤
if tag.strip()
- 這是條件判斷,確保只保留非空標籤。
- 當
tag.strip()
為空字符串(""
)時,條件為False
,該標籤會被忽略。 - 例如:
" Python ".strip() -> "Python" # 保留
" ".strip() -> "" # 忽略
列表生成式
整段代碼組合起來:
[tag.strip() for tag in tags_input.split(",") if tag.strip()]
- 這是一個清理標籤的高效方法,會:
- 對每個標籤去掉空格。
- 忽略空的標籤。
- 將結果以列表形式返回。
範例結果
輸入:
tags_input = " Python , Django, , Web Development , "
過程:
tags_input.split(",")
產生:[" Python ", " Django", " ", " Web Development ", " "]
- 列表生成式逐一處理:
" Python ".strip()
->"Python"
-> 保留。" Django".strip()
->"Django"
-> 保留。" ".strip()
->""
-> 忽略。" Web Development ".strip()
->"Web Development"
-> 保留。" ".strip()
->""
-> 忽略。
- 最終結果:
["Python", "Django", "Web Development"]
為什麼需要兩次保存?
在 Django 的多對多模型中,保存分為兩步:
- 保存主對象:
- 使用
service.save()
,確保主對象存在於資料庫中,並生成 ID。
- 使用
- 保存多對多關係:
- 使用
form.save_m2m()
,將多對多關係正確寫入資料庫。
- 使用
範例:
# 1. 創建並保存主對象
service = form.save(commit=False)
service.freelancer_user = request.user
service.save()
# 2. 保存多對多關係
service.tags.set(tags) # 設置標籤
form.save_m2m() # 確保所有多對多關係保存
這兩步缺一不可:
- 如果少了
service.save()
,多對多關係無法正確建立(因為主對象沒有 ID)。 - 如果少了
form.save_m2m()
,多對多數據不會寫入資料庫。
結語
處理 Django 的標籤功能時,需要特別注意多對多關係的保存邏輯:
- 使用
commit=False
創建對象,設置必要字段後保存。 - 設置多對多關係,如標籤與分類。
- 調用
form.save_m2m()
,確保多對多數據正確保存。
透過這種方法,你可以靈活地管理標籤與其他多對多字段,確保程式碼清晰且易於維護。
希望這篇教學能幫助你更好地理解 Django 的保存邏輯!