“透過 Django ListView 和 DetailView,輕鬆實現資料查詢,
最大化開發生產力!”

如果在前面的文章中了解了 Django 的基本 View 類和 FormView,那麼現在是時候更具體地探索 通用視圖(Generic Views) 了。
ListViewDetailView 特別針對 ‘資料輸出’ 而設計,能夠通過簡單的設置快速實現列表頁面和詳細頁面。


1. 什麼是 ListView 和 DetailView?

  1. ListView

    • 提供將模型(Model)中的多個對象以列表形式展示的功能。

    • 可以輕鬆應用分頁(Pagination)、排序和搜尋功能,幾乎可以在所有的“列表頁面”中使用。

  2. DetailView

    • 顯示特定對象(如:文章、商品、用戶等)的詳細信息。

    • 通過 URL 參數或 pk(主鍵)值來查找該對象,並能在模板中輕鬆渲染。

這兩個類別是 Django 提供的 通用視圖 的一部分,能夠快速實現 CRUD 中的“讀取”功能,是強大的工具。


2. ListView 的基本結構和使用方法

ListView 處理流程圖

2.1 基本例子

# views.py
from django.views.generic import ListView
from .models import Post

class PostListView(ListView):
    model = Post
    template_name = 'post_list.html'
    context_object_name = 'posts'  # 在模板中使用的上下文變數名(默認值:object_list)
    # paginate_by = 10  # 每頁顯示的條目數(可選)

    # 當需要額外調整查詢集時使用 get_queryset
    def get_queryset(self):
        # 例:如果只想顯示最新的文章?
        return Post.objects.order_by('-created_at')
  • model: 指定要展示哪個模型的數據。

  • template_name: 模板文件路徑(默認值:<app_name>/<model_name>_list.html)。

  • context_object_name: 在模板中使用的上下文對象名稱(默認值:object_list)。

  • paginate_by: 可以指定每頁顯示的對象數量(分頁處理)。

2.2 在 urls.py 中連接 ListView

# urls.py
from django.urls import path
from .views import PostListView

urlpatterns = [
    path('posts/', PostListView.as_view(), name='post_list'),
]

2.3 基本模板範例

<!-- post_list.html -->
<!DOCTYPE html>
<html>
<head>
    <title>文章列表</title>
</head>
<body>
    <h1>文章列表</h1>
    <ul>
        {% for post in posts %}
            <li>
                <a href="{% url 'post_detail' post.pk %}">{{ post.title }}</a>
            </li>
        {% endfor %}
    </ul>

    <!-- 實現分頁時 -->
    {% if is_paginated %}
      <div>
        {% if page_obj.has_previous %}
          <a href="?page={{ page_obj.previous_page_number }}">上一頁</a>
        {% endif %}
        <span>頁面 {{ page_obj.number }} / {{ page_obj.paginator.num_pages }}</span>
        {% if page_obj.has_next %}
          <a href="?page={{ page_obj.next_page_number }}">下一頁</a>
        {% endif %}
      </div>
    {% endif %}
</body>
</html>

核心關鍵詞: “列表頁面自動化”,“分頁(Pagination)”,“高效數據排列”


3. DetailView 的基本結構和使用方法

3.1 基本例子

# views.py
from django.views.generic import DetailView
from .models import Post

class PostDetailView(DetailView):
    model = Post
    template_name = 'post_detail.html'
    context_object_name = 'post'  # 在模板中使用的上下文變數名(默認值:object)

    # (可選)如果希望在 URL 中使用非 pk 的其他識別符,可以重載 get_slug_field等。
  • model: 查詢特定模型的單一對象。

  • template_name: 模板文件路徑(默認值:<app_name>/<model_name>_detail.html)。

  • context_object_name: 在模板中使用的上下文對象名稱(默認值:object)。

  • 如果 URL 模式中包含 <int:pk>/<slug:slug>/ 等模式,Django 會通過該值自動查找對象。

3.2 在 urls.py 中連接 DetailView

# urls.py
from django.urls import path
from .views import PostDetailView

urlpatterns = [
    path('posts/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
]

3.3 基本模板範例

<!-- post_detail.html -->
<!DOCTYPE html>
<html>
<head>
    <title>{{ post.title }}</title>
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p>撰寫日期: {{ post.created_at }}</p>
    <div>
        {{ post.content }}
    </div>
    <a href="{% url 'post_list' %}">回到列表</a>
</body>
</html>

4. 擴展 ListView & DetailView

4.1 get_queryset, get_context_data

get_queryset(): 可以客製化查詢集以符合所需條件。
例如:僅顯示某特定用戶的文章列表、關鍵字過濾、根據特定欄位排序等。

def get_queryset(self):
    queryset = super().get_queryset()
    return queryset.filter(is_active=True)

get_context_data(): 可以將額外的數據傳遞給模板。

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['extra_info'] = '附加信息'
    return context

4.2 slug、自定義查找

DetailView 可以使用 slug(短文本標識符)替代 pk,並可通過指定 slug_fieldslug_url_kwarg 進行自定義配置。

class PostDetailView(DetailView):
    model = Post
    slug_field = 'slug'           # 模型中的 slug 欄位名
    slug_url_kwarg = 'slug'       # URL 模式中接收的參數名

4.3 與 Mixin 的結合

  • LoginRequiredMixinPermissionRequiredMixin 等一起使用,可以輕鬆控制對列表和詳細頁面的訪問權限。

  • SearchMixin 這樣的自定義 Mixin,可以創建邏輯來處理搜尋參數(query),並在多個 ListView 中重用。


5. 簡單的實戰例子:實現討論區功能

以之前文章中提到的模型 Post 為基礎,來一起構建 列表 + 詳細 的功能。

# models.py
class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    slug = models.SlugField(unique=True)  # 使用 slug 範例

    def __str__(self):
        return self.title
# views.py
from django.views.generic import ListView, DetailView
from .models import Post

class PostListView(ListView):
    model = Post
    template_name = 'post_list.html'
    context_object_name = 'posts'
    paginate_by = 5  # 每頁 5 個項目

    def get_queryset(self):
        # 假設顯示的是啟用狀態的文章,而不是 slug
        return Post.objects.filter(is_active=True).order_by('-created_at')

class PostDetailView(DetailView):
    model = Post
    template_name = 'post_detail.html'
    context_object_name = 'post'
    slug_field = 'slug'
    slug_url_kwarg = 'slug'
# urls.py
urlpatterns = [
    path('posts/', PostListView.as_view(), name='post_list'),
    path('posts/<slug:slug>/', PostDetailView.as_view(), name='post_detail'),
]
  • 列表頁面: 可以通過更改頁碼,如 /posts/?page=2 來查看列表。

  • 詳細頁面: 使用 posts/<slug>/ 路徑來搜尋特定文章,並在模板中顯示。


6. 與 FBV 的比較:列表/詳細頁面到底有多容易實現?

類別 FBV (函式基礎視圖) ListView/DetailView (CBV)
代碼結構 需分別編寫列表/詳細函數,分頁、排序等需要自行實現 僅通過設置屬性,即可輕鬆客製化列表、分頁、排序、搜尋等功能
可讀性/維護性 條件語句、迴圈在多處交錯,隨著項目增大變得更複雜 列表/詳細邏輯分離,擴展(方法重寫)明確,維護方便
開發生產力(關鍵詞) “代碼重複增加,耗時” “縮短開發時間(Time-saving),提升生產力(Productivity boost)”
模板數據傳遞 需要手動構建上下文字典以傳遞給 render 基本自動傳遞上下文(object_list, object)。額外數據也可以在 get_context_data 中輕鬆處理
分頁、搜尋等高級功能 需要編寫單獨邏輯 可以輕鬆啟用的內建功能和屬性

FBV vs DetailView – 簡化的對象查詢


7. 結論及下篇文章預告

Django ListViewDetailView 透過簡化“數據列表”和“詳細頁面”的實現,給予了開發者極大的便利。
透過自動化常用功能如分頁、上下文數據附加、根據 URL 參數進行對象查詢等,可以在 代碼重用性和開發生產力 上獲得巨大優勢。

CBV(類別基礎視圖)系列將會繼續!
在下篇文章中,我們將探索如何使用 CreateViewUpdateViewDeleteView 方便地構建 CRUD 邏輯。


再次查看前一篇文章

  1. 類別基礎視圖(CBV)探索系列 #1 – 從 FBV 到 CBV 的原因和開發者的心態

  2. 類別基礎視圖(CBV)探索系列 #2 – 理解 Django 的基本 View 類

  3. 類別基礎視圖(CBV)探索系列 #3 – 使用 FormView 來輕鬆處理表單


“利用 Django 的 ListView 和 DetailView,
迅速查詢數據、輕鬆開發,並獲得更高的生產力(Productivity)!”