浏览知识库目录

Python

使用 DRF 开发 RESTful API

使用 DRF 开发 RESTful API

Django REST framework(DRF)为 Django 增加序列化、API 视图、认证、权限、分页和可浏览 API。本篇为留言项目提供一套带对象权限的 JSON API。


一、安装与注册

python -m pip install djangorestframework

settings.py

INSTALLED_APPS = [
    # Django 与项目应用
    "rest_framework",
]

建议把 API 代码放在应用内部的 api/ 子包,或在规模较小时使用 serializers.pyapi_views.py


二、Serializer

Serializer 负责模型对象与 JSON 数据之间的转换,同时承担输入校验。

# notes/serializers.py
from rest_framework import serializers

from .models import Message


class MessageSerializer(serializers.ModelSerializer):
    owner = serializers.CharField(source="owner.username", read_only=True)

    class Meta:
        model = Message
        fields = [
            "id",
            "owner",
            "name",
            "content",
            "status",
            "created_at",
            "updated_at",
        ]
        read_only_fields = ["id", "owner", "created_at", "updated_at"]

    def validate_content(self, value):
        value = value.strip()
        if len(value) < 10:
            raise serializers.ValidationError("内容至少需要 10 个字符。")
        return value

与 ModelForm 一样,应显式列出字段,并把 owner、时间等服务端字段设为只读。


三、ViewSet 与 Router

# notes/api_views.py
from django.db.models import Q
from rest_framework import permissions, viewsets

from .models import Message
from .serializers import MessageSerializer


class MessageViewSet(viewsets.ModelViewSet):
    serializer_class = MessageSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    def get_queryset(self):
        queryset = Message.objects.select_related("owner")
        if self.request.user.is_authenticated:
            return queryset.filter(
                Q(status=Message.Status.PUBLISHED) | Q(owner=self.request.user)
            ).distinct()
        return queryset.filter(status=Message.Status.PUBLISHED)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

路由:

# notes/api_urls.py
from rest_framework.routers import DefaultRouter

from .api_views import MessageViewSet

router = DefaultRouter()
router.register("messages", MessageViewSet, basename="message")

urlpatterns = router.urls

项目根路由:

path("api/", include("notes.api_urls")),

ModelViewSet 默认提供列表、创建、详情、完整更新、部分更新和删除。业务不允许某些操作时,应使用更小的 Generic View 或组合 Mixin,而不是暴露后再靠前端隐藏。


四、对象级权限

实现“所有人可读,只有作者可修改”:

# notes/permissions.py
from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.owner == request.user

视图中:

permission_classes = [
    permissions.IsAuthenticatedOrReadOnly,
    IsOwnerOrReadOnly,
]

对象权限通常在详情操作时检查。列表接口还需要在 get_queryset() 中限制可见数据,不能期待对象权限自动逐条过滤列表。


五、分页、搜索与排序

settings.py

REST_FRAMEWORK = {
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 20,
    "DEFAULT_FILTER_BACKENDS": [
        "rest_framework.filters.SearchFilter",
        "rest_framework.filters.OrderingFilter",
    ],
}

ViewSet:

search_fields = ["name", "content", "owner__username"]
ordering_fields = ["created_at", "updated_at"]
ordering = ["-created_at"]

请求示例:

GET /api/messages/?search=django&ordering=-created_at&page=2

复杂字段筛选可引入 django-filter,并明确允许筛选的字段。


六、认证选择

  • SessionAuthentication:同域网页和可浏览 API,配合 CSRF。
  • Token/JWT:移动端或独立前端常用,需规划刷新、撤销、存储和过期策略。
  • OAuth2/OIDC:接入统一身份平台或第三方登录。

不要自己发明密码哈希、签名和令牌协议。无论何种认证,生产 API 都应使用 HTTPS。

可浏览 API 登录路由:

path("api-auth/", include("rest_framework.urls")),

七、自定义 Action

from rest_framework import permissions, status
from rest_framework.decorators import action
from rest_framework.response import Response


    @action(
        detail=True,
        methods=["post"],
        permission_classes=[permissions.IsAuthenticated],
    )
    def publish(self, request, pk=None):
        message = self.get_object()
        if message.owner != request.user:
            return Response(
                {"detail": "无权发布。"},
                status=status.HTTP_403_FORBIDDEN,
            )

        message.status = Message.Status.PUBLISHED
        message.save(update_fields=["status", "updated_at"])
        return Response(self.get_serializer(message).data)

自定义 Action 适合发布、归档、取消等不属于标准 CRUD 的资源动作。不要把所有 RPC 操作都堆进同一个 ViewSet。


八、状态码与错误

  • 200 OK:成功读取或更新。
  • 201 Created:创建成功。
  • 204 No Content:删除成功且无响应体。
  • 400 Bad Request:输入校验失败。
  • 401 Unauthorized:未通过认证。
  • 403 Forbidden:已识别身份但无权限。
  • 404 Not Found:资源不存在或不应向当前用户暴露。
  • 429 Too Many Requests:请求过于频繁。

API 错误结构应保持稳定,便于前端统一展示和监控。


九、API 测试

from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.test import APITestCase

User = get_user_model()


class MessageAPITests(APITestCase):
    def test_authenticated_user_can_create_message(self):
        user = User.objects.create_user("api-user", password="pass-12345")
        self.client.force_authenticate(user)

        response = self.client.post("/api/messages/", {
            "name": "API 留言",
            "content": "这是一条通过 API 创建的留言",
            "status": "published",
        })

        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data["owner"], "api-user")

十、本篇检查清单

  • 能用 Serializer 校验并转换数据。
  • 能用 ViewSet 与 Router 创建标准资源 API。
  • 能同时实施视图级和对象级权限。
  • 能配置分页、搜索和排序。
  • 理解 Session、Token/JWT 和 OAuth 的适用场景。

上一篇:测试、调试与日志 | 下一篇:缓存、性能优化与异步任务