2025年6月20日 星期五

Django 初學者教學:打造簡單部落格應用程式 (Windows 環境)

Django 初學者教學:打造功能完整的部落格應用程式 (Windows 環境)

本教學將引導您在 Windows 環境下,從零開始使用 Django 框架構建一個功能完整的部落格應用程式。您將學會如何實現文章的創建、展示、編輯、刪除,以及搭建基礎的管理後台和進行樣式美化。本教學面向初學者,步驟詳細,並包含常見問題的解決方案。


第一部分:環境設置

在開始 Django 開發之前,我們需要確保您的 Windows 環境已正確配置。

1.1 安裝 Python

Python 是 Django 的基石,請依照以下步驟安裝:

  1. 下載 Python: 訪問 python.org 下載最新版本的 Python 安裝程式。強烈建議選擇 3.10 或以上版本,以獲得更好的兼容性和最新功能。
  2. 執行安裝程式: 運行下載的安裝程式。在安裝介面中,務必勾選「Add Python to PATH」選項。這將允許您在命令提示字元 (Command Prompt) 中直接使用 pythonpip 命令,是後續步驟的關鍵。
  3. 驗證安裝: 安裝完成後,開啟命令提示字元(按下 Win + R 鍵,輸入 cmd,然後按 Enter)。輸入以下指令確認 Python 和 pip 已成功安裝:
    Bash
    python --version
    pip --version
    
    您應該會看到類似 Python 3.10.xpip 23.x.x 的輸出。

1.2 設置虛擬環境

虛擬環境 (Virtual Environment) 是 Python 開發的最佳實踐,它能有效隔離不同專案的依賴套件,避免版本衝突。

  1. 創建虛擬環境: 在您希望存放專案的父目錄下(例如 D:\Projects),開啟命令提示字元,執行以下指令:
    Bash
    python -m venv myenv
    
    這將在當前目錄下創建一個名為 myenv 的資料夾,其中包含了虛擬環境所需的所有檔案。
  2. 啟動虛擬環境: 執行以下指令啟動您剛才創建的虛擬環境:
    Bash
    myenv\Scripts\activate
    
    成功啟動後,您的命令提示字元前會顯示 (myenv),這表示您已進入虛擬環境。在此環境下安裝的任何套件都只會影響當前專案。
  3. 安裝 Django: 在虛擬環境啟動的狀態下,使用 pip 安裝 Django 框架:
    Bash
    pip install django
    
    安裝完成後,您可以輸入 pip list 來查看已安裝的套件,確認 Django 是否已成功列出。

1.3 安裝文字編輯器

一個優秀的程式碼編輯器能大幅提升您的開發效率。

  1. 推薦使用 Visual Studio Code (VS Code):
    • 下載並安裝: 訪問 code.visualstudio.com 下載並依照指示安裝 VS Code。
    • 安裝擴充套件: 在 VS Code 中,前往「擴充套件」視圖 (左側邊欄方形圖示,或按下 Ctrl+Shift+X),搜尋並安裝以下推薦的擴充套件,它們將為 Django 開發提供強大的輔助功能,如語法高亮、程式碼補全等:
      • Python (Microsoft 官方提供)
      • Django Template (提供 Django 模板語法高亮和補全)
      • Python IntelliSense (通常包含在 Python 擴充套件中,提供智能感知)

第二部分:創建 Django 專案

環境準備就緒後,我們將開始創建 Django 專案。

2.1 創建主專案

  1. 進入專案目錄: 在命令提示字元中,切換到您希望存放 Django 專案的目錄(例如 D:\Projects):

    Bash
    cd D:\Projects
    
  2. 創建 Django 專案: 執行以下指令,創建一個名為 myblog 的 Django 專案:

    Bash
    django-admin startproject myblog
    

    這將生成一個 myblog 資料夾,其內部結構如下:

    myblog/
    ├── manage.py
    └── myblog/
        ├── __init__.py
        ├── asgi.py
        ├── settings.py
        ├── urls.py
        └── wsgi.py
    
    • manage.py:一個命令行工具,用於與您的 Django 專案進行互動。
    • myblog/ (內部資料夾):專案的核心配置檔案。
  3. 進入專案目錄: 進入新創建的專案目錄:

    Bash
    cd myblog
    

2.2 創建應用程式

Django 專案通常由多個「應用程式 (App)」組成,每個應用程式負責一項獨立的功能。我們將為部落格功能創建一個名為 blog 的應用程式。

  1. 創建應用程式:myblog 專案目錄下,執行以下指令:

    Bash
    python manage.py startapp blog
    

    這會在 myblog 專案根目錄下生成一個 blog 資料夾,其中包含模型 (models)、視圖 (views) 等關鍵檔案。

  2. 註冊應用程式: 為了讓 Django 知道 blog 應用程式的存在,您需要將其註冊到專案的配置中。打開 myblog/settings.py 檔案,在 INSTALLED_APPS 列表中添加 'blog'

    Python
    # myblog/settings.py
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'blog',  # <-- 添加這一行
    ]
    

2.3 設置語言與時區

為了讓您的部落格介面顯示中文並使用正確的時區,請修改 myblog/settings.py 檔案:

Python
# myblog/settings.py

LANGUAGE_CODE = 'zh-hant'  # 設置為繁體中文
TIME_ZONE = 'Asia/Taipei'  # 設置為台灣時區 (UTC+8)

USE_I18N = True # 啟用國際化 (Internationalization)
USE_TZ = True   # 啟用時區感知 (Time Zone awareness)

第三部分:設計資料庫模型

模型 (Models) 定義了您應用程式的資料結構,它們是 Python 類,Django 會將它們映射到資料庫的資料表。

3.1 定義文章模型

打開 blog/models.py 檔案,定義一個 Post 模型來儲存部落格文章的相關資訊:

Python
# blog/models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200, verbose_name='標題')
    content = models.TextField(verbose_name='內容')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='創建時間')
    updated_at = models.DateTimeField(auto_now=True, verbose_name='更新時間')

    def __str__(self):
        """
        返回模型的字符串表示,方便在管理後台識別。
        """
        return self.title

    class Meta:
        """
        模型的元數據選項。
        """
        verbose_name = '文章'
        verbose_name_plural = '文章列表'
        ordering = ['-created_at'] # 預設按創建時間倒序排列
  • CharField:用於儲存短文本,例如文章標題,需要指定 max_length
  • TextField:用於儲存長文本,例如文章內容,沒有長度限制。
  • DateTimeField:用於儲存日期和時間。
    • auto_now_add=True:當物件首次創建時,自動設定為當前日期和時間。
    • auto_now=True:每次儲存物件時,自動更新為當前日期和時間。
  • verbose_name:在 Django 管理後台中,為模型欄位和模型本身設定更具描述性的名稱。
  • __str__ 方法:這是一個 Python 的特殊方法,當您在 Python 環境中列印 Post 物件時,它會返回文章的標題,這對於在管理後台或其他地方識別物件非常有用。
  • class Meta:用於定義模型的元數據選項,例如 verbose_nameverbose_name_plural 用於管理後台的顯示名稱,ordering 則定義了查詢時的預設排序方式。

3.2 創建與應用資料庫遷移

每當您修改了 models.py 檔案中的模型定義時,都需要執行資料庫遷移,以便 Django 能夠在資料庫中創建或修改相應的資料表。

  1. 生成遷移檔案: 在命令提示字元中,確保您位於 myblog 專案的根目錄下,執行:

    Bash
    python manage.py makemigrations
    

    這會檢查 blog 應用程式中的模型變更,並在 blog/migrations 資料夾下生成一個新的遷移檔案。

  2. 應用遷移到資料庫: 接下來,將這些變更應用到資料庫中:

    Bash
    python manage.py migrate
    

    Django 會執行所有待處理的遷移,包括它自己的內建應用程式(如用戶認證)和您的 blog 應用程式,在預設的 SQLite 資料庫中創建必要的資料表。


第四部分:設置管理後台

Django 提供了一個功能強大的管理後台,讓您可以輕鬆管理資料。

4.1 創建超級用戶

在使用管理後台之前,您需要創建一個擁有所有權限的超級用戶 (Superuser)。

  1. 創建超級用戶: 在命令提示字元中,執行:
    Bash
    python manage.py createsuperuser
    
    按照提示輸入您的用戶名、電子郵件地址(可留空)和密碼。請記住這些憑證,它們將用於登入管理後台。

4.2 註冊模型到管理後台

為了在管理後台管理 Post 模型中的文章,您需要將 Post 模型註冊到管理網站。打開 blog/admin.py 檔案:

Python
# blog/admin.py

from django.contrib import admin
from .models import Post

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'created_at', 'updated_at')  # 在列表頁顯示的欄位
    list_filter = ('created_at',)  # 列表頁的過濾器
    search_fields = ('title', 'content')  # 列表頁的搜尋欄位
    date_hierarchy = 'created_at'  # 提供日期層次導航
    ordering = ('-created_at',) # 指定後台列表的預設排序
  • @admin.register(Post):這是一個裝飾器,用於將 Post 模型註冊到管理網站。
  • PostAdmin 類:繼承自 admin.ModelAdmin,允許您自定義模型在管理後台的顯示和行為。
    • list_display:定義在模型列表頁面中顯示的欄位。
    • list_filter:添加側邊欄過濾器,允許您根據特定欄位篩選資料。
    • search_fields:添加搜尋框,允許您根據指定欄位搜尋資料。
    • date_hierarchy:為日期欄位添加一個方便的日期層次導航(例如按年、月、日篩選)。
    • ordering:指定在管理後台列表頁面的預設排序方式。

第五部分:創建視圖與路由

視圖 (Views) 負責處理用戶請求並返回響應,而路由 (URLs) 則將 URL 模式映射到相應的視圖。

5.1 創建視圖函數

打開 blog/views.py 檔案,創建以下視圖函數來處理文章列表、詳情、創建和編輯功能:

Python
# blog/views.py

from django.shortcuts import render, get_object_or_404, redirect
from django.urls import reverse # 用於反向解析 URL
from .models import Post
from .forms import PostForm # 我們將在下一節定義這個表單

def post_list(request):
    """
    顯示所有部落格文章的列表,按創建時間倒序排列。
    """
    posts = Post.objects.all().order_by('-created_at')
    return render(request, 'blog/post_list.html', {'posts': posts})

def post_detail(request, pk):
    """
    顯示單一部落格文章的詳細內容。
    使用 get_object_or_404,如果文章不存在則返回 404 錯誤。
    """
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/post_detail.html', {'post': post})

def post_create(request):
    """
    處理新文章的創建。
    如果收到 POST 請求,則處理表單提交;否則顯示空表單。
    """
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save() # 保存表單數據到資料庫
            return redirect('post_detail', pk=post.pk) # 重定向到新創建文章的詳情頁
    else:
        form = PostForm() # 顯示一個空表單
    return render(request, 'blog/post_form.html', {'form': form, 'page_title': '新增文章'})

def post_edit(request, pk):
    """
    處理文章的編輯。
    如果收到 POST 請求,則處理表單提交;否則顯示已存在的文章數據。
    """
    post = get_object_or_404(Post, pk=pk)
    if request.method == 'POST':
        form = PostForm(request.POST, instance=post) # 使用 instance 參數預填充表單
        if form.is_valid():
            form.save() # 更新資料庫中的文章
            return redirect('post_detail', pk=post.pk) # 重定向到編輯後的文章詳情頁
    else:
        form = PostForm(instance=post) # 顯示帶有文章當前數據的表單
    return render(request, 'blog/post_form.html', {'form': form, 'post': post, 'page_title': '編輯文章'})

def post_delete(request, pk):
    """
    處理文章的刪除。
    只響應 POST 請求以防止意外刪除。
    """
    post = get_object_or_404(Post, pk=pk)
    if request.method == 'POST':
        post.delete()
        return redirect('post_list')
    # 可以選擇渲染一個確認刪除的頁面,這裡簡化為直接重定向
    return redirect('post_detail', pk=post.pk) # 如果不是 POST 請求,重定向回詳情頁
  • render(request, template_name, context):一個快捷函數,結合了加載模板、填充上下文和返回 HttpResponse 的功能。
  • get_object_or_404(model, **kwargs):一個非常實用的快捷函數,嘗試根據給定的條件從資料庫中獲取物件。如果物件不存在,則會自動引發 Http404 異常。
  • redirect(to, *args, **kwargs):重定向到另一個 URL。當成功處理表單提交後,通常會使用 redirect 來避免重複提交。
  • reverse(): 用於根據視圖名稱和參數,動態生成 URL。

5.2 創建表單

Django 的表單 (Forms) 功能可以幫助您處理 HTML 表單的生成、驗證和處理。在 blog 資料夾下創建一個新檔案 forms.py

Python
# blog/forms.py

from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    """
    基於 Post 模型創建的表單,用於創建和編輯文章。
    """
    class Meta:
        model = Post
        fields = ['title', 'content'] # 選擇在表單中包含的欄位
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '請輸入文章標題'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10, 'placeholder': '請輸入文章內容'}),
        }
        labels = {
            'title': '文章標題',
            'content': '文章內容',
        }
        help_texts = {
            'title': '限200字',
        }
        error_messages = {
            'title': {
                'max_length': '標題長度不能超過200字。'
            }
        }
  • forms.ModelForm:這是一個特殊的表單類,它可以直接基於 Django 模型生成表單。
  • class Meta
    • model:指定表單所基於的模型。
    • fields:一個列表或元組,指定要在表單中包含哪些模型欄位。
    • widgets:允許您自定義表單欄位的 HTML 控件(例如將 CharField 渲染為 TextInput,並添加 CSS 類)。
    • labels:為表單欄位提供自定義的顯示標籤。
    • help_texts:提供額外的提示訊息。
    • error_messages:自定義錯誤訊息。

5.3 設置 URL 路由

我們需要在兩個地方設置 URL 路由:專案級別的 urls.py 和應用程式級別的 urls.py

  1. 專案級別路由 (myblog/urls.py):

    打開 myblog/urls.py 檔案,將所有 blog 應用程式的 URL 模式包含進來。

    Python
    # myblog/urls.py
    
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls), # Django 管理後台的 URL
        path('', include('blog.urls')), # 包含 blog 應用程式的所有 URL 模式
    ]
    
    • include('blog.urls'):這會告訴 Django,當 URL 路徑匹配到空字串 ('') 時,去 blog 應用程式的 urls.py 檔案中尋找更詳細的 URL 模式。
  2. 應用程式級別路由 (blog/urls.py):

    在 blog 資料夾下創建一個新檔案 urls.py:

    Python
    # blog/urls.py
    
    from django.urls import path
    from . import views # 從當前目錄導入 views
    
    app_name = 'blog' # 定義應用程式命名空間,避免 URL 名稱衝突
    
    urlpatterns = [
        path('', views.post_list, name='post_list'), # 文章列表頁
        path('post/<int:pk>/', views.post_detail, name='post_detail'), # 文章詳情頁 (使用主鍵 pk)
        path('post/new/', views.post_create, name='post_create'), # 新增文章頁
        path('post/<int:pk>/edit/', views.post_edit, name='post_edit'), # 編輯文章頁
        path('post/<int:pk>/delete/', views.post_delete, name='post_delete'), # 刪除文章功能
    ]
    
    • app_name = 'blog':這定義了一個應用程式命名空間。當您在模板或視圖中使用 {% url %} 標籤或 reverse() 函數時,可以使用 blog:post_list 這樣的格式來明確指定應用程式的 URL,避免不同應用程式間的 URL 名稱衝突。
    • <int:pk>:這是一個 URL 模式中的轉換器,它會捕獲一個整數值(Primary Key,主鍵),並將其作為關鍵字參數傳遞給視圖函數。

第六部分:創建模板與靜態文件

模板 (Templates) 定義了網頁的 HTML 結構,靜態文件 (Static Files) 則包括 CSS、JavaScript 和圖片等。

6.1 設置模板目錄

blog 資料夾下,創建一個名為 templates 的資料夾,然後在 templates 內部再創建一個名為 blog 的資料夾。所有部落格相關的 HTML 模板都將放在 blog/templates/blog/ 下。這種結構是 Django 的最佳實踐,有助於組織模板並避免命名衝突。

blog/
├── templates/
│   └── blog/
│       ├── base.html
│       ├── post_list.html
│       ├── post_detail.html
│       └── post_form.html
└── ...

6.2 創建基礎模板 (base.html)

blog/templates/blog/ 中創建 base.html。這個模板將作為所有其他部落格頁面的基礎,包含共同的 HTML 結構、導航欄和外部 CSS/JS 引用。

HTML
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}我的部落格{% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
    {% load static %} {# 加載 staticfiles 模組,允許使用 static 標籤 #}
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="{% url 'blog:post_list' %}">我的部落格</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="navbarNav">
                <ul class="navbar-nav ms-auto"> {# ms-auto 將項目推到右邊 #}
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'blog:post_create' %}">新增文章</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'admin:index' %}">管理後台</a> {# 連結到 Django 管理後台 #}
                    </li>
                </ul>
            </div>
        </div>
    </nav>

    <div class="container mt-4">
        {% block content %}
        {# 子模板將在這裡填充內容 #}
        {% endblock %}
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
  • {% load static %}:必須在模板文件的開頭加載 staticfiles 模板標籤,這樣才能使用 {% static %} 標籤來引用靜態文件。
  • {% block title %}{% endblock %}:定義了一個可被子模板重寫的區塊。
  • {% block content %}:主要內容區塊,所有頁面的獨特內容將填充到這裡。
  • {% url 'blog:post_list' %}:使用 url 模板標籤來動態生成 URL。blog: 指定了應用程式命名空間,post_list 是在 blog/urls.py 中定義的 URL 名稱。

6.3 創建文章列表模板 (post_list.html)

blog/templates/blog/ 中創建 post_list.html

HTML
{% extends 'blog/base.html' %} {# 繼承 base.html 模板 #}

{% block title %}文章列表 - 我的部落格{% endblock %} {# 重寫 title 區塊 #}

{% block content %}
    <h1 class="mb-4">部落格文章列表</h1>

    {% if posts %} {# 檢查是否有文章 #}
        {% for post in posts %}
            <div class="card mb-4 shadow-sm">
                <div class="card-body">
                    <h2 class="card-title">
                        <a href="{% url 'blog:post_detail' post.pk %}">{{ post.title }}</a>
                    </h2>
                    <p class="card-text text-muted">
                        發布時間:{{ post.created_at|date:"Y年m月d日 H:i" }}
                    </p>
                    <p class="card-text">{{ post.content|truncatewords:50 }}</p> {# 截斷內容顯示前50個詞 #}
                    <div class="d-flex justify-content-between align-items-center">
                        <a href="{% url 'blog:post_detail' post.pk %}" class="btn btn-sm btn-outline-info">閱讀更多</a>
                        <a href="{% url 'blog:post_edit' post.pk %}" class="btn btn-sm btn-outline-primary">編輯文章</a>
                        <form action="{% url 'blog:post_delete' post.pk %}" method="post" onsubmit="return confirm('確定要刪除這篇文章嗎?');">
                            {% csrf_token %}
                            <button type="submit" class="btn btn-sm btn-outline-danger">刪除文章</button>
                        </form>
                    </div>
                </div>
            </div>
        {% endfor %}
    {% else %}
        <div class="alert alert-info" role="alert">
            目前沒有任何文章。
            <a href="{% url 'blog:post_create' %}" class="alert-link">立即新增第一篇文章!</a>
        </div>
    {% endif %}
{% endblock %}
  • {% extends 'blog/base.html' %}:指定該模板繼承自 base.html
  • {{ post.content|truncatewords:50 }}:Django 模板過濾器,將文章內容截斷為前 50 個詞。
  • {{ post.created_at|date:"Y年m月d日 H:i" }}:Django 模板過濾器,將日期時間格式化。
  • 刪除按鈕:使用一個包含 {% csrf_token %}<form> 標籤來發送 POST 請求以執行刪除操作,並添加 JavaScript 確認提示。

6.4 創建文章詳情模板 (post_detail.html)

blog/templates/blog/ 中創建 post_detail.html

HTML
{% extends 'blog/base.html' %}

{% block title %}{{ post.title }} - 我的部落格{% endblock %}

{% block content %}
    <h1 class="mb-4">{{ post.title }}</h1>
    <p class="text-muted mb-3">
        發布時間:{{ post.created_at|date:"Y年m月d日 H:i" }}
        {% if post.created_at != post.updated_at %}
            (更新於:{{ post.updated_at|date:"Y年m月d日 H:i" }})
        {% endif %}
    </p>
    <div class="lead mb-4">
        {{ post.content|linebreaksbr }} {# 將換行符轉換為 <br> 標籤 #}
    </div>

    <div class="d-flex justify-content-between mt-5">
        <a href="{% url 'blog:post_edit' post.pk %}" class="btn btn-primary">編輯文章</a>
        <a href="{% url 'blog:post_list' %}" class="btn btn-secondary">返回列表</a>
        <form action="{% url 'blog:post_delete' post.pk %}" method="post" onsubmit="return confirm('確定要刪除這篇文章嗎?此操作無法恢復!');">
            {% csrf_token %}
            <button type="submit" class="btn btn-danger">刪除文章</button>
        </form>
    </div>
{% endblock %}
  • {{ post.content|linebreaksbr }}:一個模板過濾器,它會將文本中的換行符 (\n) 轉換為 HTML 的 <br> 標籤,以便內容能夠正確分段顯示。

6.5 創建表單模板 (post_form.html)

blog/templates/blog/ 中創建 post_form.html

HTML
{% extends 'blog/base.html' %}

{% block title %}{{ page_title }} - 我的部落格{% endblock %} {# 使用 page_title 上下文變數 #}

{% block content %}
    <h1 class="mb-4">{{ page_title }}</h1>

    <form method="post">
        {% csrf_token %} {# 這是 Django 提供的跨站請求偽造保護 #}

        {% for field in form %}
            <div class="mb-3">
                {{ field.label_tag }} {# 顯示欄位標籤 #}
                {{ field }} {# 顯示欄位輸入框 #}
                {% if field.help_text %}
                    <div class="form-text text-muted">{{ field.help_text }}</div>
                {% endif %}
                {% for error in field.errors %}
                    <div class="text-danger small">{{ error }}</div> {# 顯示欄位錯誤訊息 #}
                {% endfor %}
            </div>
        {% endfor %}

        {% if form.non_field_errors %} {# 顯示非欄位錯誤 (例如表單整體錯誤) #}
            <div class="alert alert-danger" role="alert">
                {% for error in form.non_field_errors %}
                    <p>{{ error }}</p>
                {% endfor %}
            </div>
        {% endif %}

        <button type="submit" class="btn btn-primary me-2">保存文章</button>
        <a href="{% url 'blog:post_list' %}" class="btn btn-secondary">取消</a>
    </form>
{% endblock %}
  • {% csrf_token %}:這是 Django 為了防止跨站請求偽造 (CSRF) 攻擊而提供的安全標籤。在所有 POST 表單中都必須包含它。
  • {{ field.label_tag }}{{ field }}:這是一種更靈活的渲染表單欄位的方式,允許您在每個欄位周圍添加 Bootstrap 樣式。
  • {{ field.help_text }}{{ field.errors }}:用於顯示表單欄位的幫助文本和驗證錯誤。
  • {{ form.non_field_errors }}:用於顯示不屬於任何特定欄位的表單級別錯誤。

6.6 設置靜態文件

  1. 創建靜態文件目錄:blog 資料夾下,創建 static/css 資料夾。

    blog/
    ├── static/
    │   └── css/
    │       └── style.css
    └── ...
    
  2. 創建自定義 CSS 檔案 (style.css):blog/static/css 中創建 style.css,並添加一些基本樣式:

    CSS
    /* blog/static/css/style.css */
    body {
        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        background-color: #f8f9fa;
        color: #333;
    }
    .container {
        max-width: 960px; /* 限制容器最大寬度 */
    }
    .navbar-brand {
        font-weight: bold;
        font-size: 1.5rem;
    }
    .card {
        border: none;
        border-radius: 0.75rem;
        transition: all 0.3s ease;
        box-shadow: 0 4px 8px rgba(0,0,0,0.05); /* 輕微的陰影效果 */
    }
    .card:hover {
        transform: translateY(-3px); /* 鼠標懸停時輕微上浮 */
        box-shadow: 0 6px 12px rgba(0,0,0,0.1);
    }
    .card-title a {
        color: #007bff;
        text-decoration: none;
    }
    .card-title a:hover {
        text-decoration: underline;
    }
    .btn {
        border-radius: 0.5rem;
    }
    .alert-info {
        background-color: #e2f2ff;
        border-color: #bee5eb;
        color: #0c5460;
    }
    
  3. 配置靜態文件路徑: 打開 myblog/settings.py,確保以下靜態文件設置正確:

    Python
    # myblog/settings.py
    
    STATIC_URL = '/static/' # 靜態文件 URL 前綴
    
    # 定義 Django 尋找靜態文件的額外路徑
    STATICFILES_DIRS = [
        BASE_DIR / "blog/static",
        # 如果您有其他應用程式的靜態文件,也可以添加到這裡
    ]
    
    # STATIC_ROOT = BASE_DIR / "staticfiles" # 部署時收集靜態文件的目錄
    
    • STATIC_URL:這是用於引用靜態文件的 URL 前綴(例如,您的 CSS 文件將通過 /static/css/style.css 訪問)。
    • STATICFILES_DIRS:告訴 Django 在哪些目錄中尋找靜態文件。BASE_DIR / "blog/static" 指定了我們在 blog 應用程式中創建的 static 資料夾。
  4. 收集靜態文件 (僅在開發環境下可選,部署時必須執行):

    儘管在開發伺服器中 Django 會自動處理靜態文件,但在部署到生產環境之前,您需要收集所有靜態文件到一個統一的目錄。在命令提示字元中執行:

    Bash
    python manage.py collectstatic
    

    這會將所有應用程式(包括 Django 內建應用程式和您自己的 blog 應用程式)中的靜態文件收集到 STATIC_ROOT 指定的目錄中(如果已設置)。


第七部分:運行與測試

現在,您的部落格應用程式已經準備就緒,可以運行並進行測試了!

  1. 啟動開發伺服器: 確保您位於 myblog 專案的根目錄下,並啟動虛擬環境,然後執行:

    Bash
    python manage.py runserver
    

    如果一切順利,您會看到伺服器已啟動的提示,通常在 http://127.0.0.1:8000/

  2. 打開瀏覽器訪問:

  3. 進行測試:

    • 使用管理後台或前台的「新增文章」表單添加幾篇測試文章。
    • 確認文章列表頁面 (首頁) 能正確顯示文章。
    • 點擊文章標題,確認詳情頁面能正確顯示完整內容。
    • 嘗試編輯文章,確認修改後內容已更新。
    • 測試刪除文章功能。
    • 確認介面樣式(例如 Bootstrap 和您的 style.css)已生效。

第八部分:常見問題與解決方案

遇到問題是學習過程中的正常部分,以下是一些常見問題及其解決方案:

  • 問題 1:運行 python manage.py runserver 時提示「port 8000 已占用」。

    • 解決方案: 這表示 8000 端口已被其他程式使用。您可以指定其他端口來啟動開發伺服器。例如,使用 8080 端口:
      Bash
      python manage.py runserver 8080
      
      然後訪問 http://127.0.0.1:8080/
  • 問題 2:模板未正確加載,顯示 TemplateDoesNotExist 錯誤。

    • 解決方案:
      1. 檢查 myblog/settings.py 中的 TEMPLATES 設置: 確保 DIRS 列表中包含您的模板目錄,並且 APP_DIRS 設置為 True。我們的配置是依賴 APP_DIRS=True 的,這會讓 Django 在每個已安裝應用程式的 templates 子目錄中尋找模板。
      2. 檢查模板檔案路徑: 確保您的 HTML 模板檔案位於 blog/templates/blog/ 目錄下,且檔案名拼寫正確。例如,post_list.html 應該在 blog/templates/blog/post_list.html
      3. 確認 render 函數中的模板路徑:views.py 中,render 函數的第二個參數應是 'blog/post_list.html' 這樣帶有應用程式名稱的相對路徑。
  • 問題 3:靜態文件(如 CSS)未生效。

    • 解決方案:
      1. 確認 {% load static %} 檢查您的 base.html 檔案開頭是否包含了 {% load static %}
      2. 檢查 settings.py 中的 STATIC_URLSTATICFILES_DIRS 確保它們配置正確,並且 STATICFILES_DIRS 指向了您創建的 blog/static 目錄。
      3. 檢查靜態檔案路徑: 確保您的 style.css 檔案位於 blog/static/css/ 目錄下。
      4. 檢查瀏覽器緩存: 有時瀏覽器會緩存舊的 CSS 文件。嘗試清除瀏覽器緩存,或在開發者工具中禁用緩存。
      5. 運行 collectstatic (雖開發環境非必須,但有助於排查問題): 在生產環境中,這是必需的步驟。在開發環境中,Django 會自動提供靜態文件,但運行一次可以確保其路徑沒有配置問題:python manage.py collectstatic
  • 問題 4:資料庫遷移失敗。

    • 解決方案:
      1. 檢查 blog/models.py 的語法錯誤: 仔細檢查 models.py 檔案是否有任何 Python 語法錯誤或 Django 模型定義的錯誤。
      2. 確認已添加到 INSTALLED_APPS 確保 blog 應用程式已在 myblog/settings.pyINSTALLED_APPS 中註冊。
      3. 重新運行遷移步驟:
        Bash
        python manage.py makemigrations blog # 僅為 blog 應用程式創建遷移
        python manage.py migrate
        
      4. 刪除遷移文件 (最後手段,需謹慎): 如果問題持續,且是開發初期,可以嘗試刪除 blog/migrations 資料夾下的所有檔案(除了 __init__.py),然後刪除項目根目錄下的 db.sqlite3 檔案(這會清空所有資料庫資料),最後重新運行 makemigrationsmigrate注意:這將刪除所有數據。

第九部分:進階建議

恭喜您成功搭建了一個基本的部落格應用程式!如果您想進一步提升應用程式的功能和用戶體驗,可以考慮以下進階建議:

  1. 添加文章分類:

    • blog/models.py 中定義一個 Category 模型,並在 Post 模型中添加一個 ForeignKey 欄位與 Category 建立關聯。
    • 在管理後台註冊 Category 模型。
    • 修改 PostForm 以包含 Category 欄位。
    • 在文章列表和詳情頁面顯示分類信息,並可以添加按分類篩選的功能。
  2. 實現分頁功能:

    • 使用 Django 內建的 Paginator 類為文章列表添加分頁功能,避免一次性加載大量文章導致頁面過長。
    • post_list 視圖中實現分頁邏輯,並在 post_list.html 模板中添加分頁導航。
  3. 部署專案:

    • 將您的 Django 應用程式部署到雲端伺服器或免費平台,如 PythonAnywhere、Heroku 或 Vercel (對於簡單的 Django 專案可能需要一些額外配置)。
    • 參考這些平台的官方部署文檔,了解如何在生產環境中配置和運行 Django 應用程式(例如使用 Gunicorn/uWSGI 和 Nginx/Apache)。
  4. 添加用戶認證與權限管理:

    • 利用 Django 內建的用戶認證系統,實現用戶註冊、登入、登出功能。
    • 限制只有登入用戶才能創建、編輯和刪除文章。
    • 可以進一步實現文章的作者功能,讓每篇文章都與一個特定的用戶關聯。
  5. 美化介面與響應式設計:

    • 除了 Bootstrap,您可以學習更多 CSS 框架(如 Tailwind CSS)或自定義 CSS,來創建更獨特和美觀的介面。
    • 確保您的網站在不同尺寸的設備上(手機、平板、桌面)都能良好顯示,實現響應式設計。
  6. 引入富文本編輯器:

    • 對於文章內容 TextField,您可以集成一個富文本編輯器(如 TinyMCE 或 CKEditor),讓用戶能夠使用 WYSIWYG(所見即所得)介面編輯文章,添加圖片、格式化文本等。

沒有留言:

張貼留言

網誌存檔