小易说IT 小易说IT

Django + DRF 完整 CRUD 示例

环境: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

访问地址:

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)