Django 初學者教學:打造功能完整的部落格應用程式 (Windows 環境)
本教學將引導您在 Windows 環境下,從零開始使用 Django 框架構建一個功能完整的部落格應用程式。您將學會如何實現文章的創建、展示、編輯、刪除,以及搭建基礎的管理後台和進行樣式美化。本教學面向初學者,步驟詳細,並包含常見問題的解決方案。
第一部分:環境設置
在開始 Django 開發之前,我們需要確保您的 Windows 環境已正確配置。
1.1 安裝 Python
Python 是 Django 的基石,請依照以下步驟安裝:
- 下載 Python: 訪問
下載最新版本的 Python 安裝程式。強烈建議選擇 3.10 或以上版本,以獲得更好的兼容性和最新功能。python.org - 執行安裝程式: 運行下載的安裝程式。在安裝介面中,務必勾選「Add Python to PATH」選項。這將允許您在命令提示字元 (Command Prompt) 中直接使用
python
和pip
命令,是後續步驟的關鍵。 - 驗證安裝: 安裝完成後,開啟命令提示字元(按下
Win + R
鍵,輸入cmd
,然後按Enter
)。輸入以下指令確認 Python 和 pip 已成功安裝: 您應該會看到類似Bashpython --version pip --version
Python 3.10.x
和pip 23.x.x
的輸出。
1.2 設置虛擬環境
虛擬環境 (Virtual Environment) 是 Python 開發的最佳實踐,它能有效隔離不同專案的依賴套件,避免版本衝突。
- 創建虛擬環境: 在您希望存放專案的父目錄下(例如
D:\Projects
),開啟命令提示字元,執行以下指令: 這將在當前目錄下創建一個名為Bashpython -m venv myenv
myenv
的資料夾,其中包含了虛擬環境所需的所有檔案。 - 啟動虛擬環境: 執行以下指令啟動您剛才創建的虛擬環境:
成功啟動後,您的命令提示字元前會顯示Bashmyenv\Scripts\activate
(myenv)
,這表示您已進入虛擬環境。在此環境下安裝的任何套件都只會影響當前專案。 - 安裝 Django: 在虛擬環境啟動的狀態下,使用 pip 安裝 Django 框架:
安裝完成後,您可以輸入Bashpip install django
pip list
來查看已安裝的套件,確認 Django 是否已成功列出。
1.3 安裝文字編輯器
一個優秀的程式碼編輯器能大幅提升您的開發效率。
- 推薦使用 Visual Studio Code (VS Code):
- 下載並安裝: 訪問
下載並依照指示安裝 VS Code。code.visualstudio.com - 安裝擴充套件: 在 VS Code 中,前往「擴充套件」視圖 (左側邊欄方形圖示,或按下
Ctrl+Shift+X
),搜尋並安裝以下推薦的擴充套件,它們將為 Django 開發提供強大的輔助功能,如語法高亮、程式碼補全等:- Python (Microsoft 官方提供)
- Django Template (提供 Django 模板語法高亮和補全)
- Python IntelliSense (通常包含在 Python 擴充套件中,提供智能感知)
- 下載並安裝: 訪問
第二部分:創建 Django 專案
環境準備就緒後,我們將開始創建 Django 專案。
2.1 創建主專案
-
進入專案目錄: 在命令提示字元中,切換到您希望存放 Django 專案的目錄(例如
D:\Projects
):Bashcd D:\Projects
-
創建 Django 專案: 執行以下指令,創建一個名為
myblog
的 Django 專案:Bashdjango-admin startproject myblog
這將生成一個
myblog
資料夾,其內部結構如下:myblog/ ├── manage.py └── myblog/ ├── __init__.py ├── asgi.py ├── settings.py ├── urls.py └── wsgi.py
manage.py
:一個命令行工具,用於與您的 Django 專案進行互動。myblog/
(內部資料夾):專案的核心配置檔案。
-
進入專案目錄: 進入新創建的專案目錄:
Bashcd myblog
2.2 創建應用程式
Django 專案通常由多個「應用程式 (App)」組成,每個應用程式負責一項獨立的功能。我們將為部落格功能創建一個名為 blog
的應用程式。
-
創建應用程式: 在
myblog
專案目錄下,執行以下指令:Bashpython manage.py startapp blog
這會在
myblog
專案根目錄下生成一個blog
資料夾,其中包含模型 (models)、視圖 (views) 等關鍵檔案。 -
註冊應用程式: 為了讓 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
檔案:
# 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
模型來儲存部落格文章的相關資訊:
# 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_name
和verbose_name_plural
用於管理後台的顯示名稱,ordering
則定義了查詢時的預設排序方式。
3.2 創建與應用資料庫遷移
每當您修改了 models.py
檔案中的模型定義時,都需要執行資料庫遷移,以便 Django 能夠在資料庫中創建或修改相應的資料表。
-
生成遷移檔案: 在命令提示字元中,確保您位於
myblog
專案的根目錄下,執行:Bashpython manage.py makemigrations
這會檢查
blog
應用程式中的模型變更,並在blog/migrations
資料夾下生成一個新的遷移檔案。 -
應用遷移到資料庫: 接下來,將這些變更應用到資料庫中:
Bashpython manage.py migrate
Django 會執行所有待處理的遷移,包括它自己的內建應用程式(如用戶認證)和您的
blog
應用程式,在預設的 SQLite 資料庫中創建必要的資料表。
第四部分:設置管理後台
Django 提供了一個功能強大的管理後台,讓您可以輕鬆管理資料。
4.1 創建超級用戶
在使用管理後台之前,您需要創建一個擁有所有權限的超級用戶 (Superuser)。
- 創建超級用戶: 在命令提示字元中,執行:
按照提示輸入您的用戶名、電子郵件地址(可留空)和密碼。請記住這些憑證,它們將用於登入管理後台。Bashpython manage.py createsuperuser
4.2 註冊模型到管理後台
為了在管理後台管理 Post
模型中的文章,您需要將 Post
模型註冊到管理網站。打開 blog/admin.py
檔案:
# 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
檔案,創建以下視圖函數來處理文章列表、詳情、創建和編輯功能:
# 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
:
# 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
。
-
專案級別路由 (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 模式。
-
應用程式級別路由 (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 引用。
<!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
:
{% 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
:
{% 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
:
{% 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 設置靜態文件
-
創建靜態文件目錄: 在
blog
資料夾下,創建static/css
資料夾。blog/ ├── static/ │ └── css/ │ └── style.css └── ...
-
創建自定義 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; }
-
配置靜態文件路徑: 打開
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
資料夾。
-
收集靜態文件 (僅在開發環境下可選,部署時必須執行):
儘管在開發伺服器中 Django 會自動處理靜態文件,但在部署到生產環境之前,您需要收集所有靜態文件到一個統一的目錄。在命令提示字元中執行:
Bashpython manage.py collectstatic
這會將所有應用程式(包括 Django 內建應用程式和您自己的
blog
應用程式)中的靜態文件收集到STATIC_ROOT
指定的目錄中(如果已設置)。
第七部分:運行與測試
現在,您的部落格應用程式已經準備就緒,可以運行並進行測試了!
-
啟動開發伺服器: 確保您位於
myblog
專案的根目錄下,並啟動虛擬環境,然後執行:Bashpython manage.py runserver
如果一切順利,您會看到伺服器已啟動的提示,通常在
http://127.0.0.1:8000/
。 -
打開瀏覽器訪問:
- 部落格首頁: [可疑連結已刪除]
- 管理後台:
(使用您之前創建的超級用戶帳號登入)http://127.0.0.1:8000/admin/ - 新增文章:
http://127.0.0.1:8000/post/new/
-
進行測試:
- 使用管理後台或前台的「新增文章」表單添加幾篇測試文章。
- 確認文章列表頁面 (首頁) 能正確顯示文章。
- 點擊文章標題,確認詳情頁面能正確顯示完整內容。
- 嘗試編輯文章,確認修改後內容已更新。
- 測試刪除文章功能。
- 確認介面樣式(例如 Bootstrap 和您的
style.css
)已生效。
第八部分:常見問題與解決方案
遇到問題是學習過程中的正常部分,以下是一些常見問題及其解決方案:
-
問題 1:運行
python manage.py runserver
時提示「port 8000 已占用」。- 解決方案: 這表示 8000 端口已被其他程式使用。您可以指定其他端口來啟動開發伺服器。例如,使用 8080 端口:
然後訪問Bashpython manage.py runserver 8080
http://127.0.0.1:8080/
。
- 解決方案: 這表示 8000 端口已被其他程式使用。您可以指定其他端口來啟動開發伺服器。例如,使用 8080 端口:
-
問題 2:模板未正確加載,顯示
TemplateDoesNotExist
錯誤。- 解決方案:
- 檢查
myblog/settings.py
中的TEMPLATES
設置: 確保DIRS
列表中包含您的模板目錄,並且APP_DIRS
設置為True
。我們的配置是依賴APP_DIRS=True
的,這會讓 Django 在每個已安裝應用程式的templates
子目錄中尋找模板。 - 檢查模板檔案路徑: 確保您的 HTML 模板檔案位於
blog/templates/blog/
目錄下,且檔案名拼寫正確。例如,post_list.html
應該在blog/templates/blog/post_list.html
。 - 確認
render
函數中的模板路徑: 在views.py
中,render
函數的第二個參數應是'blog/post_list.html'
這樣帶有應用程式名稱的相對路徑。
- 檢查
- 解決方案:
-
問題 3:靜態文件(如 CSS)未生效。
- 解決方案:
- 確認
{% load static %}
: 檢查您的base.html
檔案開頭是否包含了{% load static %}
。 - 檢查
settings.py
中的STATIC_URL
和STATICFILES_DIRS
: 確保它們配置正確,並且STATICFILES_DIRS
指向了您創建的blog/static
目錄。 - 檢查靜態檔案路徑: 確保您的
style.css
檔案位於blog/static/css/
目錄下。 - 檢查瀏覽器緩存: 有時瀏覽器會緩存舊的 CSS 文件。嘗試清除瀏覽器緩存,或在開發者工具中禁用緩存。
- 運行
collectstatic
(雖開發環境非必須,但有助於排查問題): 在生產環境中,這是必需的步驟。在開發環境中,Django 會自動提供靜態文件,但運行一次可以確保其路徑沒有配置問題:python manage.py collectstatic
。
- 確認
- 解決方案:
-
問題 4:資料庫遷移失敗。
- 解決方案:
- 檢查
blog/models.py
的語法錯誤: 仔細檢查models.py
檔案是否有任何 Python 語法錯誤或 Django 模型定義的錯誤。 - 確認已添加到
INSTALLED_APPS
: 確保blog
應用程式已在myblog/settings.py
的INSTALLED_APPS
中註冊。 - 重新運行遷移步驟:
Bash
python manage.py makemigrations blog # 僅為 blog 應用程式創建遷移 python manage.py migrate
- 刪除遷移文件 (最後手段,需謹慎): 如果問題持續,且是開發初期,可以嘗試刪除
blog/migrations
資料夾下的所有檔案(除了__init__.py
),然後刪除項目根目錄下的db.sqlite3
檔案(這會清空所有資料庫資料),最後重新運行makemigrations
和migrate
。注意:這將刪除所有數據。
- 檢查
- 解決方案:
第九部分:進階建議
恭喜您成功搭建了一個基本的部落格應用程式!如果您想進一步提升應用程式的功能和用戶體驗,可以考慮以下進階建議:
-
添加文章分類:
- 在
blog/models.py
中定義一個Category
模型,並在Post
模型中添加一個ForeignKey
欄位與Category
建立關聯。 - 在管理後台註冊
Category
模型。 - 修改
PostForm
以包含Category
欄位。 - 在文章列表和詳情頁面顯示分類信息,並可以添加按分類篩選的功能。
- 在
-
實現分頁功能:
- 使用 Django 內建的
Paginator
類為文章列表添加分頁功能,避免一次性加載大量文章導致頁面過長。 - 在
post_list
視圖中實現分頁邏輯,並在post_list.html
模板中添加分頁導航。
- 使用 Django 內建的
-
部署專案:
- 將您的 Django 應用程式部署到雲端伺服器或免費平台,如 PythonAnywhere、Heroku 或 Vercel (對於簡單的 Django 專案可能需要一些額外配置)。
- 參考這些平台的官方部署文檔,了解如何在生產環境中配置和運行 Django 應用程式(例如使用 Gunicorn/uWSGI 和 Nginx/Apache)。
-
添加用戶認證與權限管理:
- 利用 Django 內建的用戶認證系統,實現用戶註冊、登入、登出功能。
- 限制只有登入用戶才能創建、編輯和刪除文章。
- 可以進一步實現文章的作者功能,讓每篇文章都與一個特定的用戶關聯。
-
美化介面與響應式設計:
- 除了 Bootstrap,您可以學習更多 CSS 框架(如 Tailwind CSS)或自定義 CSS,來創建更獨特和美觀的介面。
- 確保您的網站在不同尺寸的設備上(手機、平板、桌面)都能良好顯示,實現響應式設計。
-
引入富文本編輯器:
- 對於文章內容
TextField
,您可以集成一個富文本編輯器(如 TinyMCE 或 CKEditor),讓用戶能夠使用 WYSIWYG(所見即所得)介面編輯文章,添加圖片、格式化文本等。
- 對於文章內容
沒有留言:
張貼留言