Django REST Framework (DRF) 入门教程 – wiki基地


拥抱 API 开发:Django REST Framework (DRF) 入门宝典

在现代 Web 开发中,应用程序之间的数据交互变得越来越重要。无论是为移动应用提供后端服务,还是为前端单页面应用 (SPA) 提供数据接口,构建一个高效、稳定且易于维护的 API (Application Programming Interface) 都是核心需求。RESTful API 作为一种常见且强大的设计风格,已经被广泛采纳。

对于 Python 和 Django 开发者而言,构建 RESTful API 的首选框架莫过于 Django REST Framework (DRF)。DRF 是 Django 官方推荐的 RESTful API 开发框架,它强大而灵活,提供了丰富的功能来简化序列化、视图处理、认证、权限、限流等 API 开发中的常见任务。

本篇文章将带你从零开始,一步步深入了解 DRF 的核心概念和使用方法,构建一个简单的 RESTful API 示例。我们将详细讲解 DRF 的主要组件,并通过实际代码演示如何构建一个用于管理书籍信息的 API。

预计阅读时间较长,内容深入,请准备好你的开发环境,跟随本文一起开启 DRF 的学习之旅吧!

第一章:API 和 RESTful 概念速览

在正式接触 DRF 之前,我们先简要回顾一下 API 和 RESTful 的基本概念。

什么是 API?

API,全称 Application Programming Interface (应用程序接口),本质上是不同软件系统之间相互通信的桥梁。它定义了一系列规则、协议和工具,允许不同的应用程序相互交互,共享数据和功能。

想象一下餐厅里的服务员。你(一个应用程序)不需要知道厨房(另一个应用程序)是如何准备食物的细节,你只需要通过菜单(API 定义的接口)告诉服务员(API 调用)你想要什么,服务员会将你的点餐传递给厨房,并将准备好的食物(数据)送给你。

什么是 RESTful API?

REST,全称 Representational State Transfer (表述性状态转移),是一种软件架构风格,由 Roy Fielding 在其博士论文中提出。RESTful API 是遵循 REST 架构原则设计的 API。虽然 REST 本身不是一个严格的标准,但它提供了一套设计原则,使得 API 更易于理解、扩展和维护。

REST 的核心原则包括:

  1. 客户端-服务器 (Client-Server):客户端和服务器职责分离,提高了独立性和可移植性。
  2. 无状态 (Stateless):服务器不存储客户端的会话状态。每个请求都包含处理该请求所需的所有信息。
  3. 统一接口 (Uniform Interface):通过一致的方式与资源进行交互,主要体现在:
    • 资源 (Resources):API 中的所有信息都被建模为资源,具有唯一的标识符(URI)。
    • 通过表述操作资源 (Manipulation of Resources Through Representations):客户端通过资源的某种表述(如 JSON、XML)来改变资源的状态。
    • 自描述消息 (Self-descriptive Messages):消息本身包含足够的信息来描述如何处理它。
    • 超媒体即应用状态的引擎 (Hypermedia as the Engine of Application State, HATEOAS):通过在响应中包含超链接,客户端可以通过链接发现并执行下一步操作(这是 RESTful 更高级的原则,初学者可以先了解概念)。
  4. 可缓存 (Cacheable):客户端或中介可以缓存响应,提高性能。
  5. 分层系统 (Layered System):服务器和客户端之间的中间层(如代理、网关)对客户端是透明的,有助于提高可伸缩性和灵活性。

在实践中,设计 RESTful API 通常意味着:

  • 使用 URI 来标识资源(例如:/books, /books/1)。
  • 使用 HTTP 方法来操作资源(GET 获取,POST 创建,PUT 更新,PATCH 部分更新,DELETE 删除)。
  • 使用 HTTP 状态码表示操作结果(例如:200 OK, 201 Created, 400 Bad Request, 404 Not Found, 500 Internal Server Error)。
  • 通常使用 JSON 或 XML 作为数据交换格式(JSON 更为流行)。

第二章:DRF 简介与环境准备

为什么选择 DRF?

DRF 是构建 Django API 的事实标准,拥有众多优势:

  • 强大且灵活:提供了丰富的功能,可以处理各种复杂的 API 需求。
  • 集成 Django:与 Django 的 ORM、URL 路由、认证系统等无缝集成。
  • 序列化器 (Serializers):简化了 Django 模型对象或其他数据结构的序列化和反序列化过程,同时提供了强大的数据验证功能。
  • 视图类 (View Classes):提供了多种视图类,从简单的 APIView 到功能强大的 ModelViewSet,极大地减少了 boilerplate 代码。
  • 认证与权限 (Authentication & Permissions):内置了多种认证和权限机制,易于控制 API 的访问。
  • 可浏览 API (Browsable API):提供了一个用户友好的 Web 界面,方便开发者测试和查看 API。
  • 文档生成:支持自动生成 API 文档。
  • 活跃的社区:庞大的用户群体和活跃的社区支持。

环境准备

在开始之前,请确保你已经安装了 Python 和 Django。如果还没有,请先安装。

  1. 创建并激活虚拟环境 (推荐)

    “`bash
    python -m venv myapi-venv

    Windows

    myapi-venv\Scripts\activate

    macOS/Linux

    source myapi-venv/bin/activate
    “`

  2. 安装 Django 和 DRF

    bash
    pip install Django djangorestframework

  3. 创建 Django 项目

    bash
    django-admin startproject myapi .

    (注意最后的 . 表示在当前目录下创建项目,如果你想在子目录下创建,可以去掉 . 并进入该子目录)

  4. 创建 Django 应用

    bash
    python manage.py startapp booksapi

    我们将使用 booksapi 这个应用来构建书籍 API。

  5. 注册应用和 DRF
    打开 myapi/settings.py 文件,在 INSTALLED_APPS 列表中添加 'booksapi''rest_framework'

    “`python

    myapi/settings.py

    INSTALLED_APPS = [
    ‘django.contrib.admin’,
    ‘django.contrib.auth’,
    ‘django.contrib.contenttypes’,
    ‘django.contrib.sessions’,
    ‘django.contrib.messages’,
    ‘django.contrib.staticfiles’,
    ‘rest_framework’, # 添加这一行
    ‘booksapi’, # 添加这一行
    ]

    … 其他设置

    “`

至此,你的 Django 项目已经配置好 DRF 了。

第三章:构建第一个 DRF API – 书籍管理

我们将构建一个简单的 API,用于管理书籍信息,包括书名 (title) 和作者 (author)。

步骤 1:定义模型 (Model)

booksapi/models.py 文件中定义 Book 模型:

“`python

booksapi/models.py

from django.db import models

class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=100)
published_date = models.DateField(null=True, blank=True) # 添加发布日期字段

def __str__(self):
    return f"{self.title} by {self.author}"

“`

运行数据库迁移:

bash
python manage.py makemigrations
python manage.py migrate

步骤 2:创建序列化器 (Serializer)

序列化器是 DRF 中最核心的概念之一。它的主要作用是:

  1. 序列化 (Serialization):将复杂的 Python 数据类型(如 Django 模型实例、查询集)转换成易于传输的格式,如 JSON、XML。
  2. 反序列化 (Deserialization):将接收到的数据(如 JSON 请求体)转换回 Python 数据类型,同时进行数据验证。

booksapi 应用目录下创建一个新的文件 serializers.py

“`python

booksapi/serializers.py

from rest_framework import serializers
from .models import Book

方法一:使用 Serializer 手动定义字段

class BookSerializer(serializers.Serializer):

id = serializers.IntegerField(read_only=True) # read_only=True 表示该字段只用于序列化输出,不用于反序列化输入

title = serializers.CharField(max_length=200)

author = serializers.CharField(max_length=100)

published_date = serializers.DateField(required=False, allow_null=True) # required=False 表示该字段非必填

def create(self, validated_data):

“””

根据 validated_data 创建并返回一个新的 Book 实例。

“””

return Book.objects.create(**validated_data)

def update(self, instance, validated_data):

“””

根据 validated_data 更新并返回现有的 Book 实例。

“””

instance.title = validated_data.get(‘title’, instance.title)

instance.author = validated_data.get(‘author’, instance.author)

instance.published_date = validated_data.get(‘published_date’, instance.published_date)

instance.save()

return instance

方法二(更常用):使用 ModelSerializer

class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
# fields = ‘all‘ # 包含模型中的所有字段
fields = [‘id’, ‘title’, ‘author’, ‘published_date’] # 或者明确指定需要包含的字段
# exclude = [‘some_field_to_exclude’] # 或者明确指定需要排除的字段

“`

ModelSerializer 是 DRF 提供的一个强大工具,它可以自动推断模型字段,并为创建和更新操作提供默认的实现(即上面的 createupdate 方法)。这大大简化了序列化器的编写,特别是在处理与模型直接对应的 API 时。我们通常推荐使用 ModelSerializer

Meta 类中,我们指定了 modelBook,并使用 fields = '__all__' 或一个字段列表来指定应该包含哪些模型字段在序列化器中。id 字段通常是自动包含的,但显式列出可以更清晰。read_only=True 对于 id 字段是必要的,因为它是在数据库中生成的,不应该由客户端提供。ModelSerializer 会自动处理这些细节。

步骤 3:创建视图 (View)

视图负责处理 API 请求,包括接收请求数据、调用序列化器进行验证和转换、与模型交互(查询、创建、更新、删除)以及生成响应。

DRF 提供了多种视图类型,从简单的函数式视图到功能丰富的类式视图。我们将从最基础的开始,逐步介绍到最常用的类型。

3.1 函数式视图 (@api_view)

可以使用 @api_view 装饰器将标准的 Django 函数式视图转换为 DRF 视图。

“`python

booksapi/views.py (示例 – 函数式视图,暂时不使用在最终项目中)

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status

from .models import Book
from .serializers import BookSerializer

@api_view([‘GET’, ‘POST’]) # 指定该视图只响应 GET 和 POST 请求
def book_list(request):
“””
列出所有书籍,或创建一个新书籍。
“””
if request.method == ‘GET’:
books = Book.objects.all()
serializer = BookSerializer(books, many=True) # many=True 表示序列化一个查询集(多个对象)
return Response(serializer.data) # serializer.data 包含了序列化后的数据

elif request.method == 'POST':
    serializer = BookSerializer(data=request.data) # request.data 是 DRF 处理后的请求体数据
    if serializer.is_valid(): # 数据验证
        serializer.save() # 保存到数据库(如果使用 ModelSerializer 会自动调用 create 或 update)
        return Response(serializer.data, status=status.HTTP_201_CREATED) # 返回创建成功的响应
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # 返回验证错误

@api_view([‘GET’, ‘PUT’, ‘PATCH’, ‘DELETE’]) # 指定该视图响应特定请求方法
def book_detail(request, pk):
“””
检索、更新或删除一个书籍实例。
“””
try:
book = Book.objects.get(pk=pk) # 根据主键获取书籍
except Book.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND) # 未找到则返回 404

if request.method == 'GET':
    serializer = BookSerializer(book) # 序列化单个对象
    return Response(serializer.data)

elif request.method == 'PUT' or request.method == 'PATCH':
    serializer = BookSerializer(book, data=request.data, partial=(request.method == 'PATCH')) # 更新时传入实例和数据
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

elif request.method == 'DELETE':
    book.delete()
    return Response(status=status.HTTP_204_NO_CONTENT) # 删除成功返回 204

“`

函数式视图对于简单的 API 接口非常适用,但随着接口数量和复杂度的增加,会产生大量的重复代码(如获取对象、检查方法、调用序列化器、处理响应)。

3.2 类式视图 (APIView)

DRF 提供了 APIView 类,它是 Django 的 View 类的子类,提供了 DRF 特有的功能(如认证、权限、解析器、渲染器等)。你可以通过定义不同的方法(get(), post(), put(), patch(), delete())来处理不同的 HTTP 请求。

“`python

booksapi/views.py (示例 – APIView,暂时不使用在最终项目中)

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.http import Http404 # Django 的异常类

from .models import Book
from .serializers import BookSerializer

class BookList(APIView):
“””
列出所有书籍,或创建一个新书籍。
“””
def get(self, request, format=None): # get 方法处理 GET 请求
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
return Response(serializer.data)

def post(self, request, format=None): # post 方法处理 POST 请求
    serializer = BookSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class BookDetail(APIView):
“””
检索、更新或删除一个书籍实例。
“””
def get_object(self, pk): # 辅助方法,用于获取书籍对象,处理 404 异常
try:
return Book.objects.get(pk=pk)
except Book.DoesNotExist:
raise Http404 # 抛出 Django 的 Http404 异常,DRF 会将其转换为 404 响应

def get(self, request, pk, format=None): # get 方法处理 GET 请求 (带 pk)
    book = self.get_object(pk)
    serializer = BookSerializer(book)
    return Response(serializer.data)

def put(self, request, pk, format=None): # put 方法处理 PUT 请求 (带 pk)
    book = self.get_object(pk)
    serializer = BookSerializer(book, data=request.data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

def patch(self, request, pk, format=None): # patch 方法处理 PATCH 请求 (带 pk)
    book = self.get_object(pk)
    serializer = BookSerializer(book, data=request.data, partial=True) # partial=True 启用部分更新
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

def delete(self, request, pk, format=None): # delete 方法处理 DELETE 请求 (带 pk)
    book = self.get_object(pk)
    book.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)

“`

类式视图相比函数式视图结构更清晰,更易于组织代码。但对于标准的 CRUD (创建、读取、更新、删除) 操作,仍然有许多重复的逻辑。

3.3 通用视图 (Generic Views)

DRF 提供了一系列通用视图类,它们建立在 APIView 之上,提供了针对常见任务的默认实现,如列表显示、详情显示、创建、更新、删除等。通过简单地设置 querysetserializer_class 属性,你就可以实现完整的 CRUD 视图。

常用的通用视图包括:

  • CreateAPIView: 只处理创建请求。
  • ListAPIView: 只处理列表显示请求。
  • RetrieveAPIView: 只处理详情显示请求。
  • UpdateAPIView: 只处理更新请求。
  • DestroyAPIView: 只处理删除请求。
  • ListCreateAPIView: 处理列表显示和创建请求。
  • RetrieveUpdateAPIView: 处理详情显示和更新请求。
  • RetrieveDestroyAPIView: 处理详情显示和删除请求。
  • RetrieveUpdateDestroyAPIView: 处理详情显示、更新和删除请求。

让我们使用通用视图来重写书籍 API:

“`python

booksapi/views.py (示例 – Generic Views,暂时不使用在最终项目中)

from rest_framework import generics

from .models import Book
from .serializers import BookSerializer

class BookListCreate(generics.ListCreateAPIView):
“””
列出所有书籍,或创建一个新书籍。
等价于函数式视图 book_list 或 APIView 的 BookList。
“””
queryset = Book.objects.all() # 指定查询集
serializer_class = BookSerializer # 指定序列化器

class BookRetrieveUpdateDestroy(generics.RetrieveUpdateDestroyAPIView):
“””
检索、更新或删除一个书籍实例。
等价于函数式视图 book_detail 或 APIView 的 BookDetail。
“””
queryset = Book.objects.all() # 指定查询集
serializer_class = BookSerializer # 指定序列化器
lookup_field = ‘pk’ # 指定用于查找对象的 URL 参数名,默认为 ‘pk’

“`

通用视图极大地减少了代码量,是我们构建 API 的重要工具。然而,对于需要将多种操作(列表、创建、详情、更新、删除)组合到同一个 URL 路径下的情况(例如 /books/ 用于列表和创建,/books/1/ 用于详情、更新和删除),我们需要定义两个不同的视图类并配置相应的 URL。这引入了 ViewSet 的概念。

3.4 视图集 (ViewSets)

ViewSet 是 DRF 中一个更高层面的抽象。它不像传统的视图那样处理特定的 HTTP 方法(get, post 等),而是处理一组相关的 操作 (actions),如 list, create, retrieve, update, partial_update, destroy

ViewSet 与 URL 的映射通常不是直接在 urls.py 中定义,而是通过 路由器 (Routers) 来完成。路由器可以自动生成 ViewSet 对应操作的 URL 配置,进一步简化 URL 配置。

最常用的视图集是 ModelViewSet,它直接继承了 GenericViewSet 和多种 Mixins(包含创建、列表、详情、更新、删除操作的实现),非常适合用于与 Django 模型直接对应的 CRUD API。

“`python

booksapi/views.py (最终版本,使用 ModelViewSet)

booksapi/views.py

from rest_framework.decorators import api_view

from rest_framework.response import Response

from rest_framework import status

from rest_framework.views import APIView

from django.http import Http404

from rest_framework import generics # 暂时注释掉之前的示例

from rest_framework import viewsets

from .models import Book
from .serializers import BookSerializer

使用 ModelViewSet,它包含了列表、创建、详情、更新、部分更新、删除等所有常用操作的实现

class BookViewSet(viewsets.ModelViewSet):
“””
A ViewSet for viewing and editing Book instances.
提供 ‘list’, ‘create’, ‘retrieve’, ‘update’, ‘partial_update’, ‘destroy’ 等操作。
“””
queryset = Book.objects.all() # 指定查询集
serializer_class = BookSerializer # 指定序列化器
# lookup_field = ‘pk’ # 默认就是 ‘pk’,如果需要使用其他字段作为查找依据,可以修改这里
# filter_backends = [filters.SearchFilter, filters.OrderingFilter] # 示例:添加过滤和排序
# search_fields = [‘title’, ‘author’] # 示例:按书名和作者搜索
# permission_classes = [permissions.IsAuthenticatedOrReadOnly] # 示例:只有认证用户才能修改

“`

ModelViewSet 通过设置 querysetserializer_class 属性,就能自动提供一个完整的 RESTful API 接口集合,包括:

  • GET /books/: list action (获取所有书籍列表)
  • POST /books/: create action (创建新书籍)
  • GET /books/{pk}/: retrieve action (获取单本书籍详情)
  • PUT /books/{pk}/: update action (更新单本书籍,全量更新)
  • PATCH /books/{pk}/: partial_update action (部分更新单本书籍)
  • DELETE /books/{pk}/: destroy action (删除单本书籍)

这大大简化了视图的编写,尤其是在构建基于模型的 CRUD API 时。

步骤 4:配置 URL

现在我们需要将视图(或视图集)映射到 URL。对于 ViewSets,我们强烈推荐使用 DRF 提供的路由器。

首先,在 booksapi 应用目录下创建一个 urls.py 文件(如果不存在):

“`python

booksapi/urls.py

from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet

创建一个路由器实例

router = DefaultRouter()

注册 ViewSet 到路由器

第一个参数是 URL 前缀

第二个参数是 ViewSet 类

第三个参数是 basename,用于生成 URL 名称,如果 queryset 提供了 .model 属性,可以省略

router.register(r’books’, BookViewSet, basename=’book’)

路由器会为 BookViewSet 生成如下 URL 模式:

/books/ -> list, create

/books/{pk}/ -> retrieve, update, partial_update, destroy

urlpatterns = [
# include(router.urls) 会包含路由器生成的所有 URL
path(”, include(router.urls)),
]

“`

然后,在项目的 urls.py (myapi/urls.py) 中包含应用的 URL 配置:

“`python

myapi/urls.py

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
path(‘admin/’, admin.site.urls),
# 将 booksapi 应用的 URL 配置包含进来,前缀为 ‘api/’
path(‘api/’, include(‘booksapi.urls’)),
# 添加 DRF 的认证登录和退出 URL,方便浏览式 API 使用
path(‘api-auth/’, include(‘rest_framework.urls’, namespace=’rest_framework’)),
]

“`

现在,我们通过简单的配置就完成了 API 的 URL 路由。DefaultRouter 自动处理了 /books//books/{pk}/ 这两种 URL 模式,并将不同的 HTTP 请求方法映射到 BookViewSet 中对应的 action 上。

同时,我们在项目级 urls.py 中添加了 path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))。这会添加一个登录和退出视图,主要用于 DRF 的 Browsable API,让你可以在浏览器中方便地进行认证。

第四章:测试你的 API – 浏览式 API

DRF 最棒的功能之一就是它的浏览式 API (Browsable API)。当你用浏览器访问一个 DRF 视图的 URL 时,如果你的浏览器接受 text/html 格式的响应(默认情况下浏览器都会接受),DRF 会渲染一个用户友好的 HTML 页面,方便你查看和与 API 交互。

  1. 运行开发服务器

    bash
    python manage.py runserver

  2. 访问 API URL
    打开浏览器,访问 http://127.0.0.1:8000/api/books/

    你应该能看到一个漂亮的 HTML 页面,显示当前的图书列表(初始是空的)。

    • 查看列表 (GET): 页面上方会显示当前 URL (http://127.0.0.1:8000/api/books/) 和允许的 HTTP 方法 (GET, POST)。页面中央会显示当前的书籍列表数据(一个空的 JSON 数组 [])。
    • 创建书籍 (POST): 在页面底部的表单区域,你可以看到一个表单,其中包含 title, author, published_date 字段。填写信息后,点击 “POST” 按钮,即可向 /api/books/ 发送一个 POST 请求来创建一本新书。成功后,页面会刷新并显示新创建的书籍数据,状态码通常是 201 Created。
    • 查看详情 (GET): 创建几本书后,在列表页面点击任意一本书的 URL (例如 http://127.0.0.1:8000/api/books/1/),会跳转到该书籍的详情页面。这里你可以看到该书籍的详细信息。
    • 更新书籍 (PUT/PATCH): 在详情页面底部,会有一个表单用于更新。
      • PUT: 如果你修改表单并点击 PUT,会发送一个 PUT 请求。PUT 通常用于全量更新,即使你只修改一个字段,也需要提供所有字段的值。
      • PATCH: 如果你修改表单并点击 PATCH,会发送一个 PATCH 请求。PATCH 用于部分更新,你只需要提供需要修改的字段。DRF 的 ModelSerializerModelViewSet 默认支持 PATCH。
    • 删除书籍 (DELETE): 在详情页面,你会看到一个 DELETE 按钮。点击它可以删除当前书籍。成功后通常返回 204 No Content 状态码。

这个 Browsable API 是一个非常强大的调试和测试工具,让你在开发过程中无需借助额外的工具(如 Postman 或 curl)就能方便地与你的 API 交互。

第五章:深入理解 DRF 组件

序列化器 (Serializers) 的更多细节

除了基本的序列化和反序列化,序列化器还负责数据验证。当调用 serializer.is_valid() 方法时,它会检查传入的数据是否符合序列化器定义的字段类型、长度、必填性等要求。如果验证失败,serializer.errors 会包含详细的错误信息。

你还可以在序列化器中添加自定义的验证逻辑:

  • 字段级验证:定义 validate_<field_name> 方法。

    “`python
    class BookSerializer(serializers.ModelSerializer):
    class Meta:
    model = Book
    fields = ‘all

    def validate_title(self, value):
        """
        验证书名是否已经存在。
        """
        # self.instance 在更新操作时是当前实例,创建操作时是 None
        if self.instance is None and Book.objects.filter(title=value).exists():
            raise serializers.ValidationError("这本书已经存在了。")
        # 如果是更新操作,并且书名没有改变,则跳过检查
        if self.instance and self.instance.title == value:
            pass
        elif Book.objects.filter(title=value).exists():
             raise serializers.ValidationError("这本书名已经被使用了。")
        return value
    

    “`

  • 对象级验证:定义 validate 方法。

    “`python
    class BookSerializer(serializers.ModelSerializer):
    class Meta:
    model = Book
    fields = ‘all

    def validate(self, data):
        """
        检查作者和书名是否相同(一个无意义的示例)。
        """
        if data.get('title') == data.get('author'):
             raise serializers.ValidationError("书名和作者不能一样。")
        return data
    

    “`

序列化器是 DRF 数据处理的核心,理解并能熟练运用它对于构建高质量的 API 至关重要。

视图集 (ViewSets) 和路由器 (Routers) 的工作原理

ModelViewSet 继承自 GenericViewSet 和一些 Mixin 类:

python
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
pass

每个 Mixin 都提供了一个或多个 action 的实现:

  • CreateModelMixin: 提供 create() action。
  • ListModelMixin: 提供 list() action。
  • RetrieveModelMixin: 提供 retrieve() action。
  • UpdateModelMixin: 提供 update() action。
  • DestroyModelMixin: 提供 destroy() action。

GenericViewSet 提供了基础的属性(如 queryset, serializer_class, lookup_field)和方法(如 get_object(), get_queryset(), get_serializer(), get_serializer_class())。

路由器的工作就是检查 ViewSet 中定义的 action(例如 list, create, retrieve 等),并根据这些 action 自动创建 URL 模式,将 HTTP 方法映射到对应的 action 上。

例如,DefaultRouter 对于 BookViewSet (queryset=Book.objects.all()) 会生成类似如下的 URL 模式:

“`python

伪代码,展示路由器生成的大致映射关系

/books/

url(r’^books/$’, BookViewSet.as_view({‘get’: ‘list’, ‘post’: ‘create’}), name=’book-list’),

/books/{pk}/

url(r’^books/(?P[^/.]+)/$’, BookViewSet.as_view({‘get’: ‘retrieve’, ‘put’: ‘update’, ‘patch’: ‘partial_update’, ‘delete’: ‘destroy’}), name=’book-detail’),

注意:路由器实际生成的 URL 模式更复杂,这里仅为示意

“`

ViewSet.as_view() 方法是 ViewSet 转换为常规 Django 视图的关键。它接收一个字典,将 HTTP 方法映射到 ViewSet 的 action 名称。路由器就是通过这种方式实现了 ViewSet 和 URL 的自动绑定。

使用 ViewSet 和路由器可以保持视图代码的简洁和一致性,避免手动编写大量重复的 URL 配置。

第六章:超越 CRUD – DRF 的其他功能

本教程专注于入门,但 DRF 的强大远不止于此。了解一些其他重要功能有助于你构建更健壮的 API:

认证 (Authentication)

认证是识别发出请求的客户端身份的过程。DRF 提供了多种认证方式:

  • SessionAuthentication: 基于 Django 的 session 和 cookie,适合与 Django 自带的模板或服务器端渲染的应用配合使用,也用于 Browsable API。
  • TokenAuthentication: 基于 token 的认证,客户端在请求头中发送一个唯一的 token。
  • BasicAuthentication: 基于 HTTP Basic Auth,客户端在请求头中发送用户名和密码。
  • OAuth2 (通过第三方包实现)。

你可以在 settings.py 中配置默认的认证类,或者在单个视图类中指定:

“`python

myapi/settings.py

REST_FRAMEWORK = {
‘DEFAULT_AUTHENTICATION_CLASSES’: [
‘rest_framework.authentication.SessionAuthentication’,
‘rest_framework.authentication.TokenAuthentication’,
# … 其他认证类
]
}

booksapi/views.py

from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication # 需要安装 djangorestframework-simplejwt 或类似的 token 包

class BookViewSet(viewsets.ModelViewSet):
# … queryset, serializer_class …
# 只允许认证用户访问
# permission_classes = [IsAuthenticated]
# 同时指定认证类 (会覆盖全局设置)
# authentication_classes = [TokenAuthentication]

“`

权限 (Permissions)

权限是在认证之后进行,用于确定客户端是否有权执行某个操作(例如,是否可以修改某个对象)。DRF 提供了多种权限类:

  • AllowAny: 允许任何人访问。
  • IsAuthenticated: 只允许认证用户访问。
  • IsAdminUser: 只允许管理员用户访问。
  • IsAuthenticatedOrReadOnly: 认证用户可以完全访问,未认证用户只能读取。
  • DjangoModelPermissions: 基于 Django 模型权限。
  • DjangoObjectPermissions: 基于 Django 对象权限。

你也可以编写自定义的权限类。权限类通常在视图类中通过 permission_classes 属性指定。

分页 (Pagination)

当列表数据量很大时,一次性返回所有数据会导致性能问题。DRF 提供了多种分页器:

  • PageNumberPagination: 基于页码的分页,请求中包含 ?page=N?page_size=M 参数。
  • LimitOffsetPagination: 基于偏移量和限制数量的分页,请求中包含 ?limit=M&offset=N 参数。
  • CursorPagination: 基于游标的分页,适用于大型数据集,效率更高,但也更复杂。

可以在 settings.py 中设置全局默认分页器,或者在视图类中指定。

“`python

myapi/settings.py

REST_FRAMEWORK = {
# … authentication, permissions …
‘DEFAULT_PAGINATION_CLASS’: ‘rest_framework.pagination.PageNumberPagination’,
‘PAGE_SIZE’: 10 # 默认每页显示 10 条数据
}

booksapi/views.py

from rest_framework.pagination import PageNumberPagination

class LargeResultsSetPagination(PageNumberPagination):
page_size = 100
page_size_query_param = ‘page_size’
max_page_size = 1000

class BookViewSet(viewsets.ModelViewSet):
# … queryset, serializer_class …
# pagination_class = LargeResultsSetPagination # 为这个 ViewSet 指定分页器

“`

过滤和搜索 (Filtering & Searching)

DRF 可以结合 django-filter 或自带的过滤和搜索后端来为 API 添加过滤和搜索功能。

“`python

myapi/settings.py

REST_FRAMEWORK = {
# … other settings …
‘DEFAULT_FILTER_BACKENDS’: [‘rest_framework.filters.SearchFilter’, ‘rest_framework.filters.OrderingFilter’],
}

booksapi/views.py

from rest_framework import filters

class BookViewSet(viewsets.ModelViewSet):
# … queryset, serializer_class …
# 启用搜索和排序 (已在 settings 中全局设置,也可以在这里覆盖)
# filter_backends = [filters.SearchFilter, filters.OrderingFilter]
search_fields = [‘title’, ‘author’] # 指定可以搜索的字段
ordering_fields = [‘title’, ‘published_date’] # 指定可以排序的字段
# 例如:访问 /api/books/?search=python&ordering=-published_date
“`

测试

DRF 提供了 APIClient 用于方便地测试 API 视图,类似于 Django 的 Client,但专为 API 测试设计。

“`python

booksapi/tests.py (示例)

from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from .models import Book

class BookApiTests(APITestCase):
def setUp(self):
# 创建一些初始数据
Book.objects.create(title=’Book 1′, author=’Author A’)
Book.objects.create(title=’Book 2′, author=’Author B’)

def test_list_books(self):
    """
    测试获取书籍列表。
    """
    url = reverse('book-list') # 使用 basename 和 action 名称反向解析 URL
    response = self.client.get(url, format='json') # 使用 DRF 的 client 发送请求

    self.assertEqual(response.status_code, status.HTTP_200_OK)
    self.assertEqual(len(response.data), 2) # 检查返回的数据数量
    self.assertEqual(response.data[0]['title'], 'Book 1')

def test_create_book(self):
    """
    测试创建新书籍。
    """
    url = reverse('book-list')
    data = {'title': 'New Book', 'author': 'New Author'}
    response = self.client.post(url, data, format='json')

    self.assertEqual(response.status_code, status.HTTP_201_CREATED)
    self.assertEqual(Book.objects.count(), 3)
    self.assertEqual(Book.objects.get(id=response.data['id']).title, 'New Book')

def test_retrieve_book(self):
    """
    测试获取单本书籍详情。
    """
    book = Book.objects.get(title='Book 1')
    url = reverse('book-detail', args=[book.id])
    response = self.client.get(url, format='json')

    self.assertEqual(response.status_code, status.HTTP_200_OK)
    self.assertEqual(response.data['title'], 'Book 1')

# TODO: 添加 test_update_book, test_partial_update_book, test_delete_book 等测试用例

“`

编写单元测试对于保证 API 的质量和稳定性至关重要。

第七章:总结与展望

恭喜你!通过本文的学习,你已经掌握了 Django REST Framework 的核心概念和基础用法:

  • 理解了 API 和 RESTful 的基本原理。
  • 学习了如何在 Django 项目中安装和配置 DRF。
  • 掌握了 DRF 的主要组件:模型 (Model), 序列化器 (Serializer), 视图 (Views) (特别是 ModelViewSet), 和 路由器 (Router)
  • 构建了一个简单的书籍管理 API 示例。
  • 了解并使用了 DRF 强大的浏览式 API 进行测试。
  • 初步接触了 DRF 的其他高级功能,如认证、权限、分页和过滤。

这只是 DRF 的冰山一角,但已经足以让你开始构建自己的 RESTful API。下一步,你可以继续深入学习以下主题:

  • 更复杂的序列化器应用(嵌套序列化、自定义字段)。
  • 自定义 ViewSet 和 Mixins。
  • DRF 的各种渲染器 (Renderers) 和解析器 (Parsers)。
  • 更详细的认证和权限配置及自定义。
  • 限流 (Throttling)。
  • API 文档生成 (如使用 drf-yasgcoreapi)。
  • 更高级的过滤和搜索技术。
  • 如何处理文件上传。
  • API 版本控制。

DRF 是一个功能丰富且设计精良的框架,它遵循 Django 的“约定优于配置”原则,让你能够快速地构建出符合 RESTful 风格的高质量 API。通过不断实践和深入学习,你将能够利用 DRF 轻松应对各种复杂的 API 开发挑战。

希望这篇详细的教程为你打开了 DRF 的大门。现在,是时候动手实践,开始构建你自己的 API 项目了!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部