综合实践
本文通过一个省份信息管理系统综合运用前面所学的 DRF 知识,涵盖模型设计、序列化器、视图集、路由、跨域(CORS)配置等完整链路。文末附有思考题,供进一步练习。
项目目标
实现一个省份信息的 REST API,支持:
- 列表查询、单条查询、新增、修改、删除(标准 CRUD)
- 按 GDP 排序、按名称搜索
- 分页返回
- 前端(Vue + axios)跨域访问
示例数据结构:
| id | 省份 | 面积(万 km²) | 人口(亿) | GDP(万亿) |
|---|---|---|---|---|
| 1 | 广东 | 17.98 | 1.12 | 9.73 |
| 2 | 江苏 | 10.26 | 0.80 | 9.26 |
| 3 | 山东 | 15.70 | 1.00 | 7.65 |
后端实现
模型定义
# provinces/models.py
from django.db import models
class Province(models.Model):
name = models.CharField(max_length=50, verbose_name="省份名称", unique=True)
area = models.FloatField(verbose_name="占地面积(万 km²)")
population = models.FloatField(verbose_name="人口(亿)")
gdp = models.FloatField(verbose_name="GDP(万亿)")
class Meta:
db_table = "tb_province"
verbose_name = "省份"
ordering = ["-gdp"] # 默认按 GDP 降序序列化器
# provinces/serializers.py
from rest_framework import serializers
from .models import Province
class ProvinceSerializer(serializers.ModelSerializer):
class Meta:
model = Province
fields = "__all__"
extra_kwargs = {
"id": {"read_only": True},
}视图集
# provinces/views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.filters import SearchFilter, OrderingFilter
from rest_framework.pagination import PageNumberPagination
from .models import Province
from .serializers import ProvinceSerializer
class StandardPagination(PageNumberPagination):
page_size = 10
page_size_query_param = "size"
max_page_size = 100
class ProvinceViewSet(ModelViewSet):
queryset = Province.objects.all()
serializer_class = ProvinceSerializer
pagination_class = StandardPagination
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ["name"]
ordering_fields = ["id", "gdp", "population", "area"]
ordering = ["-gdp"] # 默认 GDP 降序路由配置
# provinces/urls.py
from rest_framework.routers import DefaultRouter
from . import views
router = DefaultRouter()
router.register("provinces", views.ProvinceViewSet)
urlpatterns = router.urls# 项目总路由 urls.py
from django.urls import path, include
urlpatterns = [
path("api/", include("provinces.urls")),
]跨域(CORS)配置
前后端分离时,浏览器会阻止跨域请求。DRF 推荐使用 django-cors-headers 解决:
pip install django-cors-headers# settings.py
INSTALLED_APPS = [
...
'corsheaders',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # 必须放在 CommonMiddleware 之前
'django.middleware.common.CommonMiddleware',
...
]
# 允许特定来源(生产环境替换为实际域名)
CORS_ALLOWED_ORIGINS = [
"http://localhost:5173", # Vite 开发服务器
"http://localhost:3000", # Create React App 开发服务器
]
# 或者开发阶段直接放开所有(生产禁用)
# CORS_ALLOW_ALL_ORIGINS = True也可以通过自定义响应头手动实现 CORS(不依赖第三方包):
class CORSMixin:
"""在视图类中混入,为响应添加 CORS 头"""
def finalize_response(self, request, response, *args, **kwargs):
response = super().finalize_response(request, response, *args, **kwargs)
response["Access-Control-Allow-Origin"] = "*"
response["Access-Control-Allow-Methods"] = "GET, POST, PUT, PATCH, DELETE, OPTIONS"
response["Access-Control-Allow-Headers"] = "Content-Type, Authorization"
return response前端调用示例(Vue + axios)
安装依赖:
npm create vite@latest frontend -- --template vue
cd frontend
npm install axios封装请求:
// src/api/provinces.js
import axios from 'axios'
const api = axios.create({
baseURL: 'http://127.0.0.1:8000/api/',
timeout: 5000,
})
export const getProvinces = (params) => api.get('provinces/', { params })
export const createProvince = (data) => api.post('provinces/', data)
export const updateProvince = (id, data) => api.put(`provinces/${id}/`, data)
export const deleteProvince = (id) => api.delete(`provinces/${id}/`)在组件中使用:
// src/components/ProvinceTable.vue
<script setup>
import { ref, onMounted } from 'vue'
import { getProvinces, deleteProvince } from '../api/provinces'
const provinces = ref([])
const total = ref(0)
const page = ref(1)
async function load() {
const res = await getProvinces({ page: page.value, size: 10 })
provinces.value = res.data.results
total.value = res.data.count
}
async function remove(id) {
await deleteProvince(id)
load()
}
onMounted(load)
</script>思考题
思考题 1:基础 CRUD 实现
参考本文后端示例,完成以下任务:
- 创建 Django 项目并添加省份应用。
- 根据示例数据向数据库插入初始记录(可用
python manage.py shell或 fixtures)。 - 启动 DRF 服务,通过 DRF 可视化页面(Browsable API)依次测试:
- GET
/api/provinces/— 查看列表 - POST
/api/provinces/— 新增一条(如"北京") - GET
/api/provinces/1/— 查看单条 - PATCH
/api/provinces/1/— 修改 GDP - DELETE
/api/provinces/1/— 删除
- GET
思考题 2:前端 Vue 集成与跨域
在本文后端基础上:
- 创建 Vue 项目,使用 axios 调用省份列表接口,将数据以表格形式展示。
- 为表格添加新增、编辑、删除按钮,实现完整的前端 CRUD 交互。
- 配置 CORS,确保前端(
localhost:5173)能够正常访问后端(localhost:8000)。
提示:编辑/删除操作完成后记得重新调用列表接口刷新数据。
思考题 3:GDP 排序与搜索
- 在前端表格的"GDP"列表头上添加点击事件,实现升序/降序切换(将
ordering参数附加在请求 URL 中)。 - 添加搜索框,支持按省份名称模糊搜索(将
search参数附加在请求 URL 中)。 - 搜索和排序同时生效时,参数如何组合传递?
思考题 4:认证保护写操作
要求未登录用户只能查看(GET),已登录用户才能新增/修改/删除。
- 配置
rest_framework.authtoken并为测试用户生成 Token。 - 在
ProvinceViewSet上添加权限:IsAuthenticatedOrReadOnly。 - 用 Postman 测试:不带 Token 的 GET 能否成功?不带 Token 的 POST 是否返回 401?携带正确 Token 的 POST 是否成功?
最后更新于