环境:Django 4.2 / 5.x + djangorestframework,前后端分离模式,只返回 JSON,不使用 Django 模板。 实现:图书 Book 的增删改查(CRUD),包含序列化器、视图集、路由、分页、简单权限,可直接复制跑起来。
一、安装依赖
pip install django djangorestframework
二、创建项目 & App
# 创建项目
django-admin startproject drf_crud_demo
cd drf_crud_demo
# 创建app
python manage.py startapp books
1. 注册 app 和 rest_framework
drf_crud_demo/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 新增
'rest_framework',
'books',
]
# DRF全局配置(可选)
REST_FRAMEWORK = {
# 默认分页
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
# 默认权限:未认证只读,认证后可写(演示用)
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticatedOrReadOnly'
]
}
三、模型 models.py
books/models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200, verbose_name="书名")
author = models.CharField(max_length=100, verbose_name="作者")
isbn = models.CharField(max_length=20, unique=True, verbose_name="ISBN")
publish_date = models.DateField(null=True, blank=True, verbose_name="出版日期")
price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="价格")
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
迁移数据库:
python manage.py makemigrations
python manage.py migrate
四、序列化器 serializers.py(新建文件)
books/serializers.py
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = "__all__" # 全部字段,也可以写列表 ['id','title','author'...]
五、视图 views.py(ViewSet 方式,最常用)
books/views.py
from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from .models import Book
from .serializers import BookSerializer
class BookViewSet(viewsets.ModelViewSet):
"""
ModelViewSet 一次性自带全部CRUD接口
list、retrieve、create、update、partial_update、destroy
"""
queryset = Book.objects.all().order_by("-created_at")
serializer_class = BookSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
ModelViewSet 映射关系:
GET /books/ → list 查询列表
POST /books/ → create 新增
GET /books/{id}/ → retrieve 查询单条
PUT /books/{id}/ → update 全量更新
PATCH /books/{id}/ → partial_update 局部更新
DELETE /books/{id}/ → destroy 删除
六、路由 urls.py
1. books/urls.py(新建)
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet)
urlpatterns = [
path('', include(router.urls)),
]
2. 项目总路由 drf_crud_demo/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('books.urls')),
# DRF自带可视化调试页面登录
path('api-auth/', include('rest_framework.urls')),
]
七、启动服务 & 测试
python manage.py runserver
访问地址:
DRF 调试页面自带表单,直接在浏览器里测试 CRUD,也可以用 Postman /curl
curl 测试示例
# 新增图书 POST
curl -X POST http://127.0.0.1:8000/api/books/ \
-H "Content-Type: application/json" \
-d '{
"title": "Django实战",
"author": "张三",
"isbn": "9781234567890",
"publish_date": "2026-01-01",
"price": 59.90
}'
# 查询列表 GET
curl http://127.0.0.1:8000/api/books/
# 查询单条 GET
curl http://127.0.0.1:8000/api/books/1/
# 局部更新 PATCH
curl -X PATCH http://127.0.0.1:8000/api/books/1/ \
-H "Content-Type: application/json" \
-d '{"price": 49.90}'
# 删除 DELETE
curl -X DELETE http://127.0.0.1:8000/api/books/1/
补充:如果想用 原生 APIView 版本(理解底层,不推荐生产)
ViewSet 是封装好的,APIView 是 DRF 最底层视图,适合学习原理
# books/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookSerializer
class BookAPIView(APIView):
def get(self, request):
books = Book.objects.all()
ser = BookSerializer(books, many=True)
return Response(ser.data)
def post(self, request):
ser = BookSerializer(data=request.data)
if ser.is_valid():
ser.save()
return Response(ser.data, status=status.HTTP_201_CREATED)
return Response(ser.errors, status=status.HTTP_400_BAD_REQUEST)