使用 Django 建立標籤分類頁:新手教學

更新日期: 2025 年 1 月 27 日

在開發網站時,標籤(Tag)功能可以幫助我們將內容進行分類與篩選,讓使用者快速找到相關內容。

本篇文章將以 Django 框架為例,帶你一步步實現一個簡單的標籤分類功能。

文章涵蓋路由設定、超連結生成、後端處理邏輯以及前端頁面呈現。


標籤超連結的設定

在網頁中,我們需要為每個標籤生成超連結,讓使用者能點擊該標籤查看相關內容。

程式碼範例

以下是透過 Django 模板語法生成標籤超連結的程式碼:

<div class="flex flex-wrap gap-2 mt-4">
  {% for tag in tags %}
      <a href="{% url 'categories:tag' tag.name %}">
        <span class="bg-indigo-300 hover:bg-indigo-700 text-white font-semibold py-2 px-4 rounded-full focus:outline-none text-s flex items-center">
            {{ tag.name }}
        </span>
      </a>
  {% endfor %}
</div>

關鍵點說明

  1. {% for tag in tags %}:迴圈遍歷所有標籤物件。
  2. {% url 'categories:tag' tag.name %}:透過 url 模板標籤動態生成標籤的超連結,目標是後端定義的路由。
  3. 樣式:使用 CSS 顯示每個標籤為圓角按鈕,並設定滑鼠懸停效果。

為什麼在模板中使用 tag.name 而非 tag.slug

在 Django 模板中,我們使用 tag.name 來生成標籤的超連結,這是一個關鍵的設計決策。

以下我們將針對 tag.name 的作用及為何不使用 tag.slug 進行詳細解釋,特別是在處理中文標籤的場景下。

什麼是 tag.name

tag.name 是標籤物件的屬性,用於存儲標籤的可讀名稱。

例如:

  • 如果標籤是「技術」,那麼 tag.name 的值就是 "技術"
  • 它是用於顯示給使用者的名稱,通常是易於理解且與語言相關的文字。

在模板中,tag.name 直接用於動態生成標籤的超連結:

<a href="{% url 'categories:tag' tag.name %}">

這會將 tag.name 的值嵌入到路由中,形成類似以下的 URL:

/categories/tag/技術/

為什麼不使用 tag.slug

Slug 的定義

slug 是資料庫中常見的字段,通常用於生成簡潔的 URL。

它是一種 URL 安全的字符串,通常只包含字母、數字和連字符。例如:

  • 如果標籤名稱是「技術」,slug 可能會被設為 "ji-shu"
  • 如果標籤名稱是「最新消息」,slug 可能會被設為 "zui-xin-xiao-xi"

使用 slug 的限制

雖然 slug 很適合英文字母或拼音,但在處理中文標籤時有明顯的限制:

  1. 額外生成工作
    • 每次新增標籤時,都需要將中文名稱轉換為拼音或其他格式來填充 slug 字段。
    • 這增加了開發者的工作量,並需要第三方工具(如 pinyin 軟件包)來完成轉換。
  2. 可讀性差
    • 對於使用者來說,/categories/tag/ji-shu/ 並不如 /categories/tag/技術/ 直觀,尤其是對不熟悉拼音的使用者。
  3. 搜尋引擎友好性
    • 搜尋引擎(如 Google 和百度)能很好地處理 URL 中的中文,因此直接使用中文名稱(tag.name)能提高 SEO(搜尋引擎優化)效果。
    • 中文名稱在 URL 中不需要轉碼,瀏覽器和搜尋引擎都能正常解析。

tag.name 的優勢

處理中文的天然適配

直接使用 tag.name 能讓 URL 支援中文標籤,無需額外轉換。

例如:

  • 標籤「技術」的 URL 直接生成為: /categories/tag/技術/
  • 這樣的 URL 不僅直觀,也與標籤名稱一致,對使用者更友好。
簡化開發工作

使用 tag.name 無需對標籤進行額外的 slug 化處理,簡化了開發過程,尤其是在初期設計階段。

增強 SEO 效果

當 URL 中包含有意義的中文內容時,搜尋引擎更容易將該頁面與相關關鍵字匹配,從而提高搜尋排名。

注意事項

雖然使用 tag.name 有許多優勢,但在處理 URL 時需要注意以下幾點:

  1. 防止重複名稱
    • 如果標籤名稱有重複,可能會導致 URL 混淆。因此,需在後端限制標籤名稱的唯一性。
  2. URL 安全性
    • Django 的路由處理器可以正確處理 URL 中的中文,但需要確保後端的解碼與前端一致。
  3. 長標籤的處理
    • 如果標籤名稱過長,可能會導致 URL 冗長。必要時,可以結合 slugname,如保留原始中文名稱並生成短的 URL。

後端路由設定

為了讓前端的超連結對應到正確的後端功能,我們需要設定 Django 的路由。

程式碼範例

urls.py 中新增以下內容:

from django.urls import path
from .views import all, category, tag

app_name = "categories"

urlpatterns = [
    path("", all, name="all"),  # 全部分類頁
    path("tag/<str:tag_name>/", tag, name="tag"),  # 標籤頁
    path("<int:id>/", category, name="category"),  # 單一分類頁
]

關鍵點說明

  1. path("tag/<str:tag_name>/", tag, name="tag")
    • 動態參數 <str:tag_name> 用於接收標籤名稱。
    • 路由名稱設定為 tag,供模板內使用。
  2. 其餘路由負責對應其他分類頁需求(如顯示單一分類或全部分類)。

後端 views.py 處理邏輯

當使用者點擊標籤時,我們需要處理請求並回傳相關資料。

程式碼範例

以下是 views.py 中的標籤處理函數:

def tag(request, tag_name):
    tag = get_object_or_404(Tag, name=tag_name)
    services = Service.objects.filter(tags__name__in=[tag.name]).annotate(
        average_rating=Avg("comments__rating")
    )
    for service in services:
        if request.user.is_authenticated:
            service.is_liked = Like.objects.filter(
                user=request.user, service=service
            ).exists()
        else:
            service.is_liked = False
    return render(request, "categories/tag.html", {"services": services, "tag": tag})

關鍵點說明

  1. get_object_or_404:嘗試從資料庫取得對應標籤,若不存在則回傳 404 錯誤。
  2. Service.objects.filter:篩選包含該標籤的服務。
  3. annotate(average_rating=Avg("comments__rating")):計算每項服務的平均評分。
  4. is_liked 屬性:根據使用者是否登入,檢查該服務是否已被使用者點讚。

標籤分類頁的前端呈現

最後,我們需要設計一個網頁來顯示篩選後的服務列表。

頁面設計

以下為模板 tag.html 的範例:

{% extends "shared/layout.html" %} 
{% block content %}
<div class="container mx-auto p-6">
  <h1 class="text-3xl font-bold text-center mb-8">{{ tag.name }}</h1>
  <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
    {% for service in services %}
      {% include "pages/category_card.html" %}
    {% endfor %}
  </div>
</div>
{% endblock %}

關鍵點說明

  1. {{ tag.name }}:顯示當前標籤的名稱。
  2. {% include "pages/category_card.html" %}:重複引入服務卡片模板,便於模組化管理。

卡片元件的詳細設計

服務卡片負責展示每項服務的主要資訊,包括圖片、評分、費用等。

程式碼範例

<a href="{% url 'services:service_detail' service.freelancer_user.id service.id %}" class="block">
  <div class="item overflow-hidden transition-transform duration-300 relative">
    <!-- 圖片 -->
    <img class="w-full h-48 object-cover rounded-lg" src="{% if service.photo %}{{ service.photo.url }}{% else %}https://fakeimg.pl/300{% endif %}" alt="Service Photo" />
    
    <!-- 愛心按鈕 -->
    <div id="like-button-{{ service.id }}" class="absolute top-2 right-2 z-10">
      <button id="toggle-like-btn-{{ service.id }}" class="transition shadow-sm" data-service-id="{{ service.id }}">
        {% if service.is_liked %}
        <img id="like-icon-{{ service.id }}" src="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' fill='red' stroke='white' ... />
        {% else %}
        <img id="like-icon-{{ service.id }}" src="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' fill='black' ... />
        {% endif %}
      </button>
    </div>
    
    <!-- 詳細內容 -->
    <div class="p-4">
      <p class="text-gray-800 font-medium truncate" title="{{ service.title }}">{{ service.title }}</p>
      <p class="text-lg text-gray-500">⭐ {{ service.average_rating|floatformat:1 }}</p>
      <p class="text-sm font-semibold text-gray-800">費用 $<span class="text-lg">{{ service.standard_price }}</span></p>
    </div>
  </div>
</a>

結語

透過上述步驟,我們實現了 Django 網站中的標籤分類功能。該功能包括標籤的超連結生成、路由設定、後端處理邏輯與前端展示。

不僅結構清晰,還具備良好的使用者體驗。希望這篇教學能幫助你快速上手 Django 的標籤功能!

Similar Posts