Django - 前端整合
Django 作為後端框架,與前端框架如 Vue 3 結合可以構建現代化的全棧應用。本節課程將教你如何將 Django 作為 RESTful API 服務,並使用 Vue 3 + Vite + TypeScript 作為前端,實現前後端分離的架構。
課程目標
- 瞭解 Django 與前端框架整合的基本原則。
- 使用 Vue 3 與 Django 的 REST API 進行數據交互。
- 設置跨域資源共享(CORS)以支持前端請求。
- 實現基本的用戶認證與數據操作功能。
課程內容
1. Django 配置跨域資源共享(CORS)
安裝 Django-CORS-Headers
後端需要允許來自前端的跨域請求:
1
pip install django-cors-headers
設定 CORS
在 settings.py
中添加:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
INSTALLED_APPS += [
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
*MIDDLEWARE,
]
# 允許所有來源(僅用於開發環境)
CORS_ALLOW_ALL_ORIGINS = True
# 如果需要限制來源,使用以下方式:
# CORS_ALLOWED_ORIGINS = [
# "http://localhost:5173", # Vue 開發伺服器
# ]
2. 前端項目初始化
使用 Vite 創建 Vue 3 專案
在終端中執行:
1
2
3
npm create vite@latest my-project --template vue-ts
cd my-project
npm install
安裝必要的依賴
1
npm install axios vue-router@4 pinia
運行開發伺服器
1
npm run dev
3. 前端項目結構
基本目錄結構如下:
1
2
3
4
5
6
7
8
9
my-project/
├── src/
│ ├── api/ # 存放 API 請求邏輯
│ ├── components/ # 通用元件
│ ├── pages/ # 各個頁面組件
│ ├── router/ # 路由設定
│ ├── stores/ # Pinia 狀態管理
│ ├── App.vue # 主應用組件
│ └── main.ts # 應用入口
4. 集成 Django REST API
配置 Axios
在 src/api/axios.ts
中:
1
2
3
4
5
6
7
8
import axios from "axios";
const apiClient = axios.create({
baseURL: "http://127.0.0.1:8000/api/", // Django API 的基礎 URL
timeout: 10000,
});
export default apiClient;
5. 實現用戶認證功能
Django 後端:API 登錄路由
在 urls.py
中添加:
1
2
3
4
5
6
7
8
9
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns += [
path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]
前端:用戶狀態管理
- 創建 Pinia Store
在src/stores/auth.ts
中:
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
import { defineStore } from "pinia";
import apiClient from "../api/axios";
export const useAuthStore = defineStore("auth", {
state: () => ({
accessToken: localStorage.getItem("accessToken") || "",
refreshToken: localStorage.getItem("refreshToken") || "",
}),
actions: {
async login(username: string, password: string) {
try {
const response = await apiClient.post("token/", { username, password });
this.accessToken = response.data.access;
this.refreshToken = response.data.refresh;
localStorage.setItem("accessToken", this.accessToken);
localStorage.setItem("refreshToken", this.refreshToken);
} catch (error) {
console.error("Login failed:", error);
}
},
logout() {
this.accessToken = "";
this.refreshToken = "";
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
},
},
});
前端:用戶登入頁面
在 src/pages/Login.vue
中:
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
<template>
<div>
<h1>登入</h1>
<form @submit.prevent="handleLogin">
<input v-model="username" placeholder="用戶名" />
<input v-model="password" type="password" placeholder="密碼" />
<button type="submit">登入</button>
</form>
</div>
</template>
<script lang="ts">
import { ref } from "vue";
import { useAuthStore } from "../stores/auth";
export default {
setup() {
const authStore = useAuthStore();
const username = ref("");
const password = ref("");
const handleLogin = async () => {
await authStore.login(username.value, password.value);
};
return { username, password, handleLogin };
},
};
</script>
6. 整合文章數據
前端:文章列表 API 請求
在 src/api/posts.ts
中:
1
2
3
4
5
6
import apiClient from "./axios";
export const fetchPosts = async () => {
const response = await apiClient.get("posts/");
return response.data;
};
前端:文章列表頁面
在 src/pages/Posts.vue
中:
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
<template>
<div>
<h1>文章列表</h1>
<ul>
<li v-for="post in posts" :key="post.id">{{ post.title }}</li>
</ul>
</div>
</template>
<script lang="ts">
import { ref, onMounted } from "vue";
import { fetchPosts } from "../api/posts";
export default {
setup() {
const posts = ref([]);
onMounted(async () => {
posts.value = await fetchPosts();
});
return { posts };
},
};
</script>
7. 配置路由
在 src/router/index.ts
中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { createRouter, createWebHistory } from "vue-router";
import Login from "../pages/Login.vue";
import Posts from "../pages/Posts.vue";
const routes = [
{ path: "/", component: Posts },
{ path: "/login", component: Login },
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
課堂練習
- 在前端實現文章的增刪改查功能,並測試 API 整合是否順暢。
- 實現用戶登出功能,清除本地存儲的 Token 並返回登入頁。
- 優化文章列表頁面,顯示每篇文章的作者與發布時間。
作業
- 整合標籤與分類數據,實現在前端顯示文章的分類與標籤。
- 實現用戶權限控制,僅允許登入用戶訪問特定頁面。
- 使用 Vue 的組件化設計,將通用功能抽取為獨立組件。
本文章以 CC BY 4.0 授權