使用 Django 建立標籤分類頁:新手教學
更新日期: 2025 年 1 月 27 日
本文為 Django 標籤功能 系列文,第 7 篇
在開發網站時,標籤(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>
關鍵點說明
{% for tag in tags %}
:迴圈遍歷所有標籤物件。{% url 'categories:tag' tag.name %}
:透過url
模板標籤動態生成標籤的超連結,目標是後端定義的路由。- 樣式:使用 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
很適合英文字母或拼音,但在處理中文標籤時有明顯的限制:
- 額外生成工作
- 每次新增標籤時,都需要將中文名稱轉換為拼音或其他格式來填充
slug
字段。 - 這增加了開發者的工作量,並需要第三方工具(如 pinyin 軟件包)來完成轉換。
- 每次新增標籤時,都需要將中文名稱轉換為拼音或其他格式來填充
- 可讀性差
- 對於使用者來說,
/categories/tag/ji-shu/
並不如/categories/tag/技術/
直觀,尤其是對不熟悉拼音的使用者。
- 對於使用者來說,
- 搜尋引擎友好性
- 搜尋引擎(如 Google 和百度)能很好地處理 URL 中的中文,因此直接使用中文名稱(
tag.name
)能提高 SEO(搜尋引擎優化)效果。 - 中文名稱在 URL 中不需要轉碼,瀏覽器和搜尋引擎都能正常解析。
- 搜尋引擎(如 Google 和百度)能很好地處理 URL 中的中文,因此直接使用中文名稱(
tag.name
的優勢
處理中文的天然適配
直接使用 tag.name
能讓 URL 支援中文標籤,無需額外轉換。
例如:
- 標籤「技術」的 URL 直接生成為:
/categories/tag/技術/
- 這樣的 URL 不僅直觀,也與標籤名稱一致,對使用者更友好。
簡化開發工作
使用 tag.name
無需對標籤進行額外的 slug 化處理,簡化了開發過程,尤其是在初期設計階段。
增強 SEO 效果
當 URL 中包含有意義的中文內容時,搜尋引擎更容易將該頁面與相關關鍵字匹配,從而提高搜尋排名。
注意事項
雖然使用 tag.name
有許多優勢,但在處理 URL 時需要注意以下幾點:
- 防止重複名稱
- 如果標籤名稱有重複,可能會導致 URL 混淆。因此,需在後端限制標籤名稱的唯一性。
- URL 安全性
- Django 的路由處理器可以正確處理 URL 中的中文,但需要確保後端的解碼與前端一致。
- 長標籤的處理
- 如果標籤名稱過長,可能會導致 URL 冗長。必要時,可以結合
slug
與name
,如保留原始中文名稱並生成短的 URL。
- 如果標籤名稱過長,可能會導致 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"), # 單一分類頁
]
關鍵點說明
path("tag/<str:tag_name>/", tag, name="tag")
:- 動態參數
<str:tag_name>
用於接收標籤名稱。 - 路由名稱設定為
tag
,供模板內使用。
- 動態參數
- 其餘路由負責對應其他分類頁需求(如顯示單一分類或全部分類)。
後端 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})
關鍵點說明
get_object_or_404
:嘗試從資料庫取得對應標籤,若不存在則回傳 404 錯誤。Service.objects.filter
:篩選包含該標籤的服務。annotate(average_rating=Avg("comments__rating"))
:計算每項服務的平均評分。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 %}
關鍵點說明
{{ tag.name }}
:顯示當前標籤的名稱。{% 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 的標籤功能!