文章

Django - 內容管理系統 (CMS)

內容管理系統 (Content Management System, CMS) 是一種幫助用戶創建、編輯、組織和發布內容的工具。Django 的靈活性和可擴展性,使其成為構建 CMS 的理想框架。本節課程將帶領你設計並實現一個簡單但功能完整的 CMS 系統,支援內容分類、內容編輯和使用者角色管理等功能。


課程目標

  1. 瞭解 CMS 的基本概念與設計原則。
  2. 使用 Django 構建一個簡單的 CMS 系統,包括文章、分類與標籤管理。
  3. 實現內容的版本控制與權限管理功能。

課程內容

1. CMS 基本結構設計

功能需求

  1. 管理內容(文章/頁面)。
  2. 支持分類與標籤系統。
  3. 提供簡單的權限控制(如作者、編輯者、管理員)。
  4. 支持內容狀態(如草稿、已發布)。

數據模型設計

models.py 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

class Tag(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)

    def __str__(self):
        return self.name

class Post(models.Model):
    STATUS_CHOICES = [
        ('draft', '草稿'),
        ('published', '已發布'),
    ]
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    content = models.TextField()
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
    tags = models.ManyToManyField(Tag, blank=True)
    status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    published_at = models.DateTimeField(null=True, blank=True)

    def __str__(self):
        return self.title

2. 設計管理後台界面

註冊模型

admin.py 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from django.contrib import admin
from .models import Category, Tag, Post

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ('name', 'created_at')
    prepopulated_fields = {'slug': ('name',)}

@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
    list_display = ('name',)
    prepopulated_fields = {'slug': ('name',)}

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'status', 'created_at', 'published_at')
    list_filter = ('status', 'created_at', 'author')
    search_fields = ('title', 'content')
    prepopulated_fields = {'slug': ('title',)}
    raw_id_fields = ('author',)
    date_hierarchy = 'published_at'
    ordering = ('-published_at',)

3. 添加內容版本控制

安裝 Django Simple History

1
pip install django-simple-history

修改模型以支持版本控制

models.py 中:

1
2
3
4
5
from simple_history.models import HistoricalRecords

class Post(models.Model):
    # 其他字段
    history = HistoricalRecords()

settings.py 中啟用版本控制:

1
2
INSTALLED_APPS += ['simple_history']
MIDDLEWARE += ['simple_history.middleware.HistoryRequestMiddleware']

執行遷移:

1
2
python manage.py makemigrations
python manage.py migrate

現在,後台會記錄每次內容變更的歷史版本。


4. 權限管理與工作流程

角色與權限

  • 作者:可以創建和編輯自己的文章,但無法發布。
  • 編輯者:可以編輯所有文章並負責發布。
  • 管理員:具有最高管理權限。

admin.py 中自定義權限檢查:

1
2
3
4
5
6
7
8
9
10
11
class PostAdmin(admin.ModelAdmin):
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)

    def save_model(self, request, obj, form, change):
        if not change or not obj.author_id:
            obj.author = request.user
        super().save_model(request, obj, form, change)

5. 添加 API 支持

安裝 Django REST Framework

1
pip install djangorestframework

創建 API

views.py 中:

1
2
3
4
5
6
7
from rest_framework import viewsets
from .models import Post, Category, Tag
from .serializers import PostSerializer, CategorySerializer, TagSerializer

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.filter(status='published')
    serializer_class = PostSerializer

serializers.py 中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from rest_framework import serializers
from .models import Post, Category, Tag

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = '__all__'

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = '__all__'

class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = '__all__'

設定路由

urls.py 中:

1
2
3
4
5
6
7
8
9
from rest_framework.routers import DefaultRouter
from .views import PostViewSet, CategoryViewSet, TagViewSet

router = DefaultRouter()
router.register(r'posts', PostViewSet)
router.register(r'categories', CategoryViewSet)
router.register(r'tags', TagViewSet)

urlpatterns += router.urls

課堂練習

  1. 為 CMS 系統中的每個模型設計管理後台界面。
  2. 添加版本控制功能,並在後台測試歷史版本的查看與恢復功能。
  3. 為文章管理功能實現簡單的 API,支持查詢發布的內容。

作業

  1. 實現文章的審核流程,支持作者提交草稿後由編輯者審核並發布。
  2. 添加 API 身份驗證功能,僅允許已認證的使用者存取。
  3. 整合 API 與前端,實現一個簡單的前後端分離的 CMS 頁面。

本文章以 CC BY 4.0 授權