新手指南|Django 標籤功能|後端資料儲存邏輯

更新日期: 2025 年 2 月 5 日

在 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()
  • 解析
    1. 從用戶輸入中獲取標籤字符串(tags_input),例如:Python, Django, Web.
    2. 使用逗號分割標籤字符串,並清理空格(tag.strip())。
    3. 調用 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 ")。
  • 空的標籤(如 ", , ")。

目標是:

  1. 去掉每個標籤的空格(如把 " Python " 轉為 "Python")。
  2. 丟棄空的標籤(如忽略 "")。

拆解代碼

分割字符串

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()]
  • 這是一個清理標籤的高效方法,會:
    1. 對每個標籤去掉空格。
    2. 忽略空的標籤。
    3. 將結果以列表形式返回。

範例結果

輸入:

tags_input = " Python , Django, , Web Development , "

過程:

  1. tags_input.split(",") 產生: [" Python ", " Django", " ", " Web Development ", " "]
  2. 列表生成式逐一處理:
    • " Python ".strip() -> "Python" -> 保留。
    • " Django".strip() -> "Django" -> 保留。
    • " ".strip() -> "" -> 忽略。
    • " Web Development ".strip() -> "Web Development" -> 保留。
    • " ".strip() -> "" -> 忽略。
  3. 最終結果: ["Python", "Django", "Web Development"]

為什麼需要兩次保存?

在 Django 的多對多模型中,保存分為兩步:

  1. 保存主對象
    • 使用 service.save(),確保主對象存在於資料庫中,並生成 ID。
  2. 保存多對多關係
    • 使用 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 的標籤功能時,需要特別注意多對多關係的保存邏輯:

  1. 使用 commit=False 創建對象,設置必要字段後保存。
  2. 設置多對多關係,如標籤與分類。
  3. 調用 form.save_m2m(),確保多對多數據正確保存。

透過這種方法,你可以靈活地管理標籤與其他多對多字段,確保程式碼清晰且易於維護。

希望這篇教學能幫助你更好地理解 Django 的保存邏輯!

Similar Posts