从零开始学习 Django REST Framework (DRF) – wiki基地


从零开始,玩转数据接口:Django REST Framework (DRF) 入门指南

在现代软件开发中,构建健壮、高效且易于维护的 Web API(应用程序接口)变得越来越重要。无论是为移动应用提供后端服务,还是为前端单页应用(SPA)提供数据支持,或者仅仅是为了与其他系统进行数据交换,API 都扮演着核心角色。

对于 Django 开发者来说,构建 RESTful API 的首选框架便是 Django REST Framework (DRF)。它是一个强大而灵活的工具包,在 Django 的基础上提供了构建 Web API 所需的几乎所有功能,极大地简化了开发流程。

如果你已经熟悉 Django 的基础知识(如模型、视图、URLs、ORM等),但对如何构建 API 一无所知,那么这篇从零开始的指南就是为你准备的。我们将一步步深入了解 DRF 的核心概念,并通过实际例子来理解它们的作用。

文章目录:

  1. 为什么选择 Django REST Framework (DRF)?
  2. 准备工作:你需要了解什么?
  3. 环境搭建:安装与配置
  4. 核心概念一:序列化器 (Serializers)
    • 序列化器的作用:数据转换与验证
    • Serializer vs ModelSerializer
    • 创建第一个 Serializer
    • 序列化与反序列化实践
  5. 核心概念二:视图 (Views)
    • APIView:API 版的 Django View
    • Generic API Views:简化常见操作
    • ViewSets:将相关逻辑打包
    • 选择适合你的视图类型
  6. 核心概念三:URLs 与路由器 (Routers)
    • 手动映射 APIView/Generic Views
    • 使用 DefaultRouter 自动映射 ViewSets
  7. 请求与响应 (Requests & Responses)
    • Request 对象:获取客户端数据
    • Response 对象:构建返回数据
    • 状态码的应用
  8. 权限与认证 (Authentication & Permissions)
    • 为何需要认证和权限?
    • 认证 (Authentication):你是谁?
      • SessionAuthentication
      • TokenAuthentication
    • 权限 (Permissions):你能做什么?
      • AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly
    • 如何在视图中应用权限和认证
  9. 进阶特性 (简要介绍)
    • 分页 (Pagination)
    • 过滤与搜索 (Filtering & Searching)
    • 限流 (Throttling)
    • 测试 API
    • API 文档
  10. 构建一个简单的 API 示例
  11. 总结与后续学习方向

1. 为什么选择 Django REST Framework (DRF)?

在深入学习之前,我们先来了解一下 DRF 的优势:

  • 强大与灵活: DRF 提供了处理 API 开发中各种场景的工具,从序列化、请求处理到认证、权限、限流、分页等一应俱全。
  • 基于 Django: 充分利用 Django 现有的优势,如 ORM、URL 路由系统、认证系统等,与 Django 项目无缝集成。
  • 社区活跃: 拥有庞大的用户群和活跃的社区,遇到问题容易找到解决方案,文档详尽。
  • 可视化 API 浏览器: 默认提供了强大的 API 浏览器界面,方便开发者测试和调试 API。
  • 广泛的应用: 被许多公司用于构建生产级的 Web API。

简单来说,如果你正在使用 Django 并需要构建 API,DRF 是最自然、最高效的选择。

2. 准备工作:你需要了解什么?

正如文章开头所说,本指南假定你已经具备以下基础:

  • Python 基础: 理解 Python 的语法、数据结构(列表、字典)、函数、类等。
  • Django 基础:
    • Django 项目结构
    • 模型的定义与使用 (ORM)
    • 基本的 Django 视图和 URL 路由
    • 了解 settings.py 配置
    • 虚拟环境 (Virtual Environment) 的使用

如果你对这些概念还不熟悉,强烈建议先花时间学习 Django 的官方教程或相关资源。

3. 环境搭建:安装与配置

首先,确保你已经在项目目录下激活了虚拟环境。如果还没有,请创建并激活一个:

“`bash

创建虚拟环境 (如果还没有)

python -m venv venv

激活虚拟环境 (Linux/macOS)

source venv/bin/activate

激活虚拟环境 (Windows)

venv\Scripts\activate
“`

接下来,安装 Django 和 Django REST Framework:

bash
pip install Django djangorestframework

安装完成后,你需要将 DRF 添加到 Django 项目的 settings.py 文件中的 INSTALLED_APPS 列表里:

“`python

your_project/settings.py

INSTALLED_APPS = [
# … other apps
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
‘rest_framework’, # <– Add this line
# … your other apps
]

… rest of settings

“`

就是这样!DRF 现在已经集成到你的 Django 项目中了。

4. 核心概念一:序列化器 (Serializers)

序列化器是 DRF 中最核心的概念之一。它的主要作用是将复杂的数据类型(如 Django 模型对象或查询集)转换为易于传输的格式(如 JSON、XML 等),以及将接收到的数据(如 JSON)验证并转换回复杂的数据类型(如 Django 模型对象)。

你可以将序列化器类比为 Django 中的 Form 或 ModelForm。Form 用于处理 HTML 表单数据,而 Serializer 用于处理 API 数据。ModelForm 可以自动根据模型生成表单,ModelSerializer 也可以自动根据模型生成序列化器。

4.1 序列化器的作用:数据转换与验证

  • 序列化 (Serialization): 将后端数据(如模型实例)转换为客户端需要的格式(通常是 Python 原生的数据类型,DRF 会将其进一步渲染成 JSON)。例如,将一个 Book 模型对象转换成一个 Python 字典:{'id': 1, 'title': '...', 'author': '...'},这个字典最终会变成 JSON 字符串。
  • 反序列化 (Deserialization): 将客户端发送的数据(通常是 JSON,DRF 会将其解析成 Python 字典)验证后,转换成后端可以处理的数据类型(如用于创建或更新模型实例)。

4.2 Serializer vs ModelSerializer

  • Serializer: 这是一个通用的序列化器类。你可以完全自定义其中的字段,不一定与任何模型绑定。适用于处理非模型数据或需要高度定制化输出/输入的场景。
  • ModelSerializer: 继承自 Serializer。它会自动根据指定的 Django 模型生成对应的序列化器字段。这是最常用的序列化器类型,可以大大减少编写重复代码。

4.3 创建第一个 Serializer (ModelSerializer)

假设你有一个 Django 应用 (my_app),其中定义了一个简单的模型 Book

“`python

my_app/models.py

from django.db import models

class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
published_date = models.DateField()
isbn = models.CharField(max_length=13, unique=True)

def __str__(self):
    return self.title

“`

为了能够通过 API 返回 Book 对象的数据,我们需要为其创建一个 ModelSerializer。在 my_app 应用目录下创建一个 serializers.py 文件:

“`python

my_app/serializers.py

from rest_framework import serializers
from .models import Book

class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
# fields = ‘all‘ # 包含模型所有字段
fields = [‘id’, ‘title’, ‘author’, ‘published_date’] # 指定包含的字段
# exclude = [‘isbn’] # 排除某些字段 (与 fields 互斥)
“`

ModelSerializer 中,我们通常在内部 Meta 类中指定 modelfields(或 exclude)。fields 指定了序列化器应该包含的模型字段。'__all__' 是一个快捷方式,表示包含模型中的所有字段。在这里,我们明确列出了希望在 API 输出中看到的字段。id 字段会自动被包含(除非你明确排除),因为它通常是资源的唯一标识符。

4.4 序列化与反序列化实践

在 Django Shell 中测试序列化器:

bash
python manage.py shell

“`python

In Django Shell

from my_app.models import Book
from my_app.serializers import BookSerializer

创建一些 Book 对象 (如果还没有)

book1 = Book.objects.create(title=’Django for Beginners’, author=’A. Developer’, published_date=’2023-01-01′, isbn=’111-1-11-111111-1′)
book2 = Book.objects.create(title=’DRF Master’, author=’B. Coder’, published_date=’2024-05-20′, isbn=’222-2-22-222222-2′)

— 序列化单个对象 —

1. 创建序列化器实例,传入要序列化的对象

serializer = BookSerializer(book1)

2. 访问 .data 属性获取序列化后的数据 (Python 字典)

print(serializer.data)

输出类似: {‘id’: 1, ‘title’: ‘Django for Beginners’, ‘author’: ‘A. Developer’, ‘published_date’: ‘2023-01-01’}

— 序列化多个对象 (查询集) —

1. 获取查询集

books = Book.objects.all()

2. 创建序列化器实例,传入查询集,并设置 many=True

serializer = BookSerializer(books, many=True)

3. 访问 .data 属性

print(serializer.data)

输出类似: [{‘id’: 1, …}, {‘id’: 2, …}]

— 反序列化 (验证和创建新对象) —

data = {‘title’: ‘New Book Title’, ‘author’: ‘New Author’, ‘published_date’: ‘2024-06-01’, ‘isbn’: ‘333-3-33-333333-3’}

1. 创建序列化器实例,传入要反序列化的数据

serializer = BookSerializer(data=data)

2. 调用 is_valid() 方法进行验证

print(serializer.is_valid()) # 返回 True 或 False

3. 如果验证通过,可以通过 .validated_data 访问验证后的数据 (Python 字典)

if serializer.is_valid():
print(serializer.validated_data)
# 4. 调用 .save() 方法创建或更新模型实例
# 对于新建,如果序列化器没有传入 instance,save() 会调用 serializer.create()
book_instance = serializer.save()
print(f”Created book: {book_instance.title}”)
else:
# 访问 .errors 属性查看验证错误信息
print(serializer.errors)

— 反序列化 (验证和更新现有对象) —

existing_book = Book.objects.get(id=1)
update_data = {‘title’: ‘Updated Title’} # 只更新 title

1. 创建序列化器实例,传入要更新的对象 (instance) 和更新数据 (data)

serializer = BookSerializer(instance=existing_book, data=update_data, partial=True) # partial=True 允许部分更新

2. 调用 is_valid() 方法进行验证

print(serializer.is_valid())

3. 如果验证通过,调用 .save() 方法

if serializer.is_valid():
# 如果序列化器传入了 instance,save() 会调用 serializer.update()
updated_book = serializer.save()
print(f”Updated book: {updated_book.title}”)

“`

is_valid() 方法是反序列化过程中必须调用的。它会检查传入的数据是否符合序列化器字段的定义(例如,字段类型、最大长度、是否必填等),并会触发模型字段的验证器。如果验证失败,serializer.errors 会包含详细的错误信息。serializer.save() 方法会根据是否传入 instance 来决定是创建新对象还是更新现有对象。

掌握序列化器是学习 DRF 的第一步,也是非常重要的一步。

5. 核心概念二:视图 (Views)

在 Django 中,视图函数/类接收 HttpRequest 对象并返回 HttpResponse 对象。DRF 中的视图也有类似的作用,但它们接收的是 DRF 的 Request 对象,并返回 DRF 的 Response 对象。DRF 的视图提供了更多的便利功能,特别是在处理 API 请求(如内容协商、解析请求体、格式化响应体)方面。

DRF 提供了多种视图类型,以适应不同的开发需求:

5.1 APIView:API 版的 Django View

rest_framework.views.APIView 是 DRF 视图的基础。它类似于 Django 的 View 类,你可以定义对应 HTTP 方法(get, post, put, delete 等)的方法。

APIView 与 Django View 的主要区别在于:

  • 接收 DRF 的 Request 对象,提供了更方便的数据访问(request.data)。
  • 返回 DRF 的 Response 对象,可以方便地返回各种格式的数据(默认为 JSON)。
  • 提供了认证、权限、限流等机制的支持。

示例:

“`python

my_app/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 BookListAPIView(APIView):
“””
List all books, or create a new book.
“””
def get(self, request, format=None):
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
return Response(serializer.data)

def post(self, request, format=None):
    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 BookDetailAPIView(APIView):
“””
Retrieve, update or delete a book instance.
“””
def get_object(self, pk):
try:
return Book.objects.get(pk=pk)
except Book.DoesNotExist:
from django.http import Http404 # DRF exception handling converts this to 404
raise Http404

def get(self, request, pk, format=None):
    book = self.get_object(pk)
    serializer = BookSerializer(book)
    return Response(serializer.data)

def put(self, request, pk, format=None):
    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 delete(self, request, pk, format=None):
    book = self.get_object(pk)
    book.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)

“`

这是一个典型的 RESTful API 实现,包含了获取列表/创建 (BookListAPIView) 和获取详情/更新/删除 (BookDetailAPIView) 的逻辑。

5.2 Generic API Views:简化常见操作

由于很多 API 视图都有相似的模式(例如,获取列表、获取详情、创建、更新、删除),DRF 提供了一组通用的视图类来减少重复代码。这些类通常与一个模型和序列化器绑定。

它们位于 rest_framework.generics 模块下:

  • ListAPIView: 只读列表。
  • CreateAPIView: 只创建。
  • RetrieveAPIView: 只读详情。
  • UpdateAPIView: 只更新。
  • DestroyAPIView: 只删除。
  • ListCreateAPIView: 列表和创建。
  • RetrieveUpdateAPIView: 详情和更新。
  • RetrieveDestroyAPIView: 详情和删除。
  • RetrieveUpdateDestroyAPIView: 详情、更新和删除。

使用 Generic Views 编写上面的 Book API 示例会更简洁:

“`python

my_app/views.py (using Generic Views)

from rest_framework import generics
from .models import Book
from .serializers import BookSerializer

class BookListCreate(generics.ListCreateAPIView):
“””
List all books or create a new book.
“””
queryset = Book.objects.all() # 指定要查询的数据集
serializer_class = BookSerializer # 指定使用的序列化器

class BookRetrieveUpdateDestroy(generics.RetrieveUpdateDestroyAPIView):
“””
Retrieve, update or delete a book instance.
“””
queryset = Book.objects.all() # 指定要查询的数据集
serializer_class = BookSerializer # 指定使用的序列化器
# 查找单个对象的字段,默认为 ‘pk’
# lookup_field = ‘isbn’ # 如果想通过 isbn 查找,则设置此项
“`

Generic Views 通过设置 querysetserializer_class 属性,或者覆盖相应的方法(如 get_queryset(), get_serializer_class())来工作。它们极大地减少了 boilerplate 代码。

5.3 ViewSets:将相关逻辑打包

ViewSets 将一组相关的视图逻辑打包到一个类中,例如,一个 ModelViewSet 通常包含了对一个模型进行 CRUD (创建、读取、更新、删除) 的所有操作。

使用 ViewSets 的主要好处是:

  • 代码组织更清晰,一个 ViewSet 对应一个资源的所有操作。
  • 可以与 DRF 的 Routers 结合使用,自动生成 URL 配置,进一步简化代码。

ViewSets 位于 rest_framework.viewsets 模块下。最常用的是 ModelViewSet,它继承自 GenericViewSet 并混合了 ListModelMixin, RetrieveModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin,从而提供了完整的 CRUD 功能。

使用 ViewSet 编写 Book API 示例:

“`python

my_app/views.py (using ViewSets)

from rest_framework import viewsets
from .models import Book
from .serializers import BookSerializer

class BookViewSet(viewsets.ModelViewSet):
“””
A ViewSet for viewing and editing Book instances.
“””
queryset = Book.objects.all()
serializer_class = BookSerializer
# lookup_field = ‘isbn’ # 如果想通过 isbn 查找,则设置此项
“`

这个 BookViewSet 类就包含了处理 /books/ (GET, POST) 和 /books/{pk}/ (GET, PUT, PATCH, DELETE) 请求的所有逻辑。

5.4 选择适合你的视图类型

  • APIView: 当你需要完全控制请求和响应处理流程,或者处理非 CRUD 的复杂逻辑时使用。
  • Generic API Views: 当你需要实现常见的 CRUD 操作,但又不想使用 ViewSets 带来的自动 URL 生成时使用,或者只需要实现部分 CRUD 操作(如只读)。
  • ViewSets: 当你需要为一个资源提供完整的 CRUD 或大部分 CRUD 操作,并希望利用 Routers 自动生成 URL 配置时,这是最简洁高效的选择。对于大多数基于模型的 API,ModelViewSet 是首选。

对于初学者来说,通常会从 APIView 开始理解基础概念,然后过渡到 Generic Views 学习如何简化代码,最后掌握 ViewSets 和 Routers 的强大组合。

6. 核心概念三:URLs 与路由器 (Routers)

定义 API 的 URL 路径是将客户端请求映射到相应的视图逻辑的必要步骤。

6.1 手动映射 APIView/Generic Views

如果你使用 APIView 或 Generic Views,你需要像处理普通 Django 视图一样,在应用的 urls.py 中手动定义 URL 模式:

“`python

my_app/urls.py (for APIView/Generic Views)

from django.urls import path

from .views import BookListAPIView, BookDetailAPIView # 如果使用 APIView

from .views import BookListCreate, BookRetrieveUpdateDestroy # 如果使用 Generic Views

urlpatterns = [
# path(‘books/’, BookListAPIView.as_view(), name=’book-list’), # APIView
# path(‘books//’, BookDetailAPIView.as_view(), name=’book-detail’), # APIView

path('books/', BookListCreate.as_view(), name='book-list-create'), # Generic Views
path('books/<int:pk>/', BookRetrieveUpdateDestroy.as_view(), name='book-detail-update-delete'), # Generic Views

]
“`

然后在项目的 urls.py 中包含这个应用的 URL 配置:

“`python

your_project/urls.py

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

urlpatterns = [
path(‘admin/’, admin.site.urls),
path(‘api/’, include(‘my_app.urls’)), # 包含应用的 API URLs
]
“`

现在,你可以访问 /api/books//api/books/{pk}/ 来与你的 API 交互了。

6.2 使用 DefaultRouter 自动映射 ViewSets

如果你使用了 ViewSets (特别是 ModelViewSet),DRF 的 Routers 可以自动为你生成 URL 配置,这极大地简化了 URLs 定义。

rest_framework.routers.DefaultRouter 是最常用的路由器。它会自动为 ModelViewSet 注册 /basename/ (list/create) 和 /basename/{pk}/ (retrieve/update/delete) 两种模式的 URL。

修改 my_app/urls.py 以使用 Router:

“`python

my_app/urls.py (for ViewSets)

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

创建一个路由器实例

router = DefaultRouter()

注册 ViewSet 到路由器,第一个参数是 URL 前缀,第二个是 ViewSet 类

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

ViewSets 生成的 URL 会通过 router.urls 属性暴露

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

项目的 urls.py 保持不变,仍然是 path(‘api/’, include(‘my_app.urls’)),

“`

router.register() 的第一个参数 r'books' 定义了 URL 的前缀(例如 /api/books/),BookViewSet 是要注册的 ViewSet 类。basename='book' 用于生成 URL 名称 (e.g., book-list, book-detail)。如果 ViewSet 设置了 queryset 属性,DRF 可以自动从模型的 _meta.model_name 推断出 basename,所以很多时候可以省略 basename 参数,除非你需要自定义它。

通过使用 Router,你无需手动为 ViewSet 的每种操作编写 URL 模式,DRF 会根据标准 RESTful 约定自动生成。这是使用 ViewSet 的一个主要驱动力。

7. 请求与响应 (Requests & Responses)

7.1 Request 对象:获取客户端数据

DRF 的 Request 对象 (rest_framework.request.Request) 是 Django HttpRequest 对象的扩展。它提供了更灵活的方式来解析客户端发送的数据,支持各种内容类型 (如 JSON, XML, 表单数据)。

主要属性:

  • request.data: 这是 Request 对象最重要的属性之一。它返回解析后的请求体数据,不区分 HTTP 方法 (GET 请求的查询参数,POST/PUT/PATCH 请求的请求体数据都会被解析)。对于 JSON 数据,它会解析成 Python 字典。
  • request.query_params: 访问 URL 中的查询参数 (?param=value)。

示例:

“`python

In a DRF view method (e.g., post, put)

def post(self, request, format=None):
# 从请求体中获取数据 (例如 POST 的 JSON 数据)
data = request.data
print(data) # {‘title’: ‘…’, ‘author’: ‘…’}

# 从 URL 查询参数中获取数据 (例如 /api/books/?format=json)
format_param = request.query_params.get('format')
print(format_param) # 'json' or None

# ... use data with serializer ...

“`

7.2 Response 对象:构建返回数据

DRF 的 Response 对象 (rest_framework.response.Response) 是 Django HttpResponse 对象的扩展。它接受任意 Python 原生数据类型(如字典、列表),然后根据客户端的 Accept 头部和视图中定义的渲染器 (Renderer) 来渲染成相应的格式(默认为 JSON)。

创建 Response 对象时,通常会传入要返回的数据和可选的状态码:

“`python
from rest_framework.response import Response
from rest_framework import status

返回成功数据

return Response({‘message’: ‘Success’, ‘data’: book_serializer.data})

返回错误数据并指定状态码

return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

返回成功并指定状态码

return Response(serializer.data, status=status.HTTP_201_CREATED)

返回无内容的成功响应 (如 DELETE 请求成功)

return Response(status=status.HTTP_204_NO_CONTENT)
“`

7.3 状态码的应用

在 API 设计中,使用正确的 HTTP 状态码非常重要,它们能告诉客户端请求的结果状态。DRF 提供了 rest_framework.status 模块,包含了常用的 HTTP 状态码常量,便于使用和提高代码可读性。

  • status.HTTP_200_OK: 请求成功
  • status.HTTP_201_CREATED: 资源创建成功 (通常用于 POST)
  • status.HTTP_204_NO_CONTENT: 请求成功但无内容返回 (通常用于 DELETE, PUT, PATCH)
  • status.HTTP_400_BAD_REQUEST: 客户端发送的数据无效
  • status.HTTP_401_UNAUTHORIZED: 未认证
  • status.HTTP_403_FORBIDDEN: 已认证但无权限
  • status.HTTP_404_NOT_FOUND: 资源不存在
  • status.HTTP_500_INTERNAL_SERVER_ERROR: 服务器内部错误

始终在 Response 中显式或隐式(默认 200 OK)地设置正确的状态码。

8. 权限与认证 (Authentication & Permissions)

保护你的 API 不被未经授权的访问是至关重要的。DRF 提供了灵活的认证和权限系统。

8.1 为何需要认证和权限?

  • 认证 (Authentication): 确定发出请求的客户端/用户是谁。它回答“你是谁?”的问题。常见的认证方式有基于 Session、Token、OAuth 等。
  • 权限 (Permissions): 确定已认证的用户是否有权执行请求的操作 (如访问某个资源、修改某个数据)。它回答“你能做什么?”的问题。

认证总是发生在权限检查之前。只有确定了请求者的身份,才能判断他是否有相应的权限。

8.2 认证 (Authentication):你是谁?

你可以在全局 settings.py 或在具体的视图类中设置认证类。

全局配置 (应用于所有视图):

“`python

settings.py

REST_FRAMEWORK = {
‘DEFAULT_AUTHENTICATION_CLASSES’: [
‘rest_framework.authentication.SessionAuthentication’, # 用于基于 Session 的认证 (配合 Django 后台登录)
‘rest_framework.authentication.TokenAuthentication’, # 用于基于 Token 的认证
# ‘rest_framework_simplejwt.authentication.JWTAuthentication’, # 如果使用 JWT
]
}
“`

视图级配置 (只应用于该视图):

“`python

my_app/views.py

from rest_framework.views import APIView
from rest_framework.authentication import TokenAuthentication, SessionAuthentication
from rest_framework.permissions import IsAuthenticated # 权限类后面讲

class ProtectedView(APIView):
authentication_classes = [SessionAuthentication, TokenAuthentication] # 指定该视图使用的认证类
permission_classes = [IsAuthenticated] # 指定该视图使用的权限类

def get(self, request):
    # request.user 会是一个 User 对象 (如果已认证) 或 AnonymousUser (如果未认证)
    content = {
        'user': str(request.user),  # User instance
        'auth': str(request.auth),  # Token instance or None
    }
    return Response(content)

“`

SessionAuthentication 依赖于 Django 内置的 session 和 cookie,常用于与传统的基于 session 的 Web 应用集成,或者在 API 浏览器中方便测试(如果你已经登录了 Django 后台)。

TokenAuthentication 是一种更常见的 API 认证方式。用户通过登录获取一个唯一的 token,后续请求在 Authorization 头部携带这个 token (Authorization: Token <your_token>)。你需要安装 rest_framework.authtoken 应用并运行迁移:

“`bash

settings.py

INSTALLED_APPS = [
# …
‘rest_framework’,
‘rest_framework.authtoken’, # Add this line
# …
]
“`

bash
python manage.py migrate

然后,你可以创建一个简单的视图供用户获取 Token:

“`python

my_app/views.py (add this)

from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response

class CustomObtainAuthToken(ObtainAuthToken):
def post(self, request, args, *kwargs):
serializer = self.serializer_class(data=request.data,
context={‘request’: request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data[‘user’]
token, created = Token.objects.get_or_create(user=user)
return Response({
‘token’: token.key,
‘user_id’: user.pk,
’email’: user.email
})

my_app/urls.py (add this to urlpatterns)

from .views import CustomObtainAuthToken

path(‘token-auth/’, CustomObtainAuthToken.as_view()),

``
客户端向
/api/token-auth/` 发送 POST 请求,包含 username 和 password,如果验证通过,将收到一个包含 token 的响应。

8.3 权限 (Permissions):你能做什么?

权限类决定了是否允许请求继续处理。它们也可以在全局或视图级别设置。

全局配置:

“`python

settings.py

REST_FRAMEWORK = {
# …
‘DEFAULT_PERMISSION_CLASSES’: [
‘rest_framework.permissions.IsAuthenticated’, # Default: Only authenticated users can access
]
}
“`

视图级配置:

“`python

my_app/views.py (using ViewSet)

from rest_framework import viewsets
from rest_framework.permissions import IsAuthenticated, AllowAny, IsAdminUser, IsAuthenticatedOrReadOnly
from .models import Book
from .serializers import BookSerializer

class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer

# permission_classes = [AllowAny] # 允许任何人访问
# permission_classes = [IsAuthenticated] # 只允许已认证用户访问
# permission_classes = [IsAdminUser] # 只允许管理员用户访问
permission_classes = [IsAuthenticatedOrReadOnly] # 已认证用户可读写,未认证用户只读

# 你也可以创建自定义权限类
# permission_classes = [IsAuthenticated, CustomPermission]

“`

常见的权限类:

  • AllowAny: 允许任何用户访问,无论是否认证。
  • IsAuthenticated: 只允许已认证用户访问。
  • IsAdminUser: 只允许 is_staffTrue 的用户访问。
  • IsAuthenticatedOrReadOnly: 已认证用户可以执行任何操作 (GET, POST, PUT, DELETE),未认证用户只能执行安全操作 (GET, HEAD, OPTIONS)。这非常常用。

你可以通过继承 rest_framework.permissions.BasePermission 创建自定义权限类,实现 has_permission(self, request, view)has_object_permission(self, request, view, obj) 方法来定义更复杂的权限逻辑。

将认证类和权限类结合使用,你就可以构建安全可靠的 API 了。例如,使用 TokenAuthentication + IsAuthenticated 要求用户必须携带有效 token 才能访问,使用 TokenAuthentication + IsAuthenticatedOrReadOnly 要求携带 token 的用户可以修改数据,不带 token 的用户只能读取数据。

9. 进阶特性 (简要介绍)

DRF 还提供了许多其他强大的功能,这里简要介绍一些常用的:

  • 分页 (Pagination): 当数据集很大时,一次性返回所有数据效率低下且占用资源。DRF 提供了多种分页方式 (如 PageNumberPagination, LimitOffsetPagination, CursorPagination),可以很方便地为列表视图添加分页功能。

    “`python

    settings.py

    REST_FRAMEWORK = {
    # …
    ‘DEFAULT_PAGINATION_CLASS’: ‘rest_framework.pagination.PageNumberPagination’,
    ‘PAGE_SIZE’: 10 # 默认每页大小
    }

    或者在视图中指定

    class BookList(generics.ListAPIView):

    queryset = Book.objects.all()

    serializer_class = BookSerializer

    pagination_class = rest_framework.pagination.LimitOffsetPagination

    ``
    * **过滤与搜索 (Filtering & Searching):** 让客户端能够根据特定条件过滤或搜索资源。DRF 支持集成
    django-filter应用实现强大的过滤功能,也内置了简单的搜索功能 (SearchFilter`)。

    “`python

    settings.py

    REST_FRAMEWORK = {
    # …
    ‘DEFAULT_FILTER_BACKENDS’: [‘django_filters.rest_framework.DjangoFilterBackend’, ‘rest_framework.filters.SearchFilter’],
    }

    安装 django-filter: pip install django-filter

    添加到 INSTALLED_APPS

    在视图中指定过滤和搜索字段

    class BookList(generics.ListAPIView):

    queryset = Book.objects.all()

    serializer_class = BookSerializer

    filterset_fields = [‘author’, ‘published_date’] # 允许通过 author 或 published_date 过滤

    search_fields = [‘title’, ‘author’] # 允许通过 title 或 author 搜索

    “`
    * 限流 (Throttling): 控制客户端在一段时间内可以发出的请求次数,防止滥用。

  • 测试 API: DRF 提供了 APIClientAPITestCase 类,方便编写针对 API 的测试。

  • API 文档: DRF 可以与 drf-yasgdjango-rest-swagger 等第三方库集成,自动生成符合 OpenAPI (Swagger) 规范的交互式 API 文档。

10. 构建一个简单的 API 示例

让我们综合前面学到的知识,快速构建一个简单的 Book API:

  1. 创建 Django 项目和应用:
    bash
    django-admin startproject myapi
    cd myapi
    python manage.py startapp my_app
  2. 配置 settings.py:
    “`python
    # myapi/settings.py
    INSTALLED_APPS = [
    # … default apps
    ‘rest_framework’,
    ‘rest_framework.authtoken’, # 如果需要 token 认证
    ‘my_app’, # 添加你的应用
    ]

    REST_FRAMEWORK = {
    ‘DEFAULT_AUTHENTICATION_CLASSES’: [
    ‘rest_framework.authentication.TokenAuthentication’,
    ‘rest_framework.authentication.SessionAuthentication’, # for browsable API
    ],
    ‘DEFAULT_PERMISSION_CLASSES’: [
    ‘rest_framework.permissions.IsAuthenticatedOrReadOnly’ # 默认权限
    ],
    # ‘DEFAULT_PAGINATION_CLASS’: ‘rest_framework.pagination.PageNumberPagination’,
    # ‘PAGE_SIZE’: 10,
    # ‘DEFAULT_FILTER_BACKENDS’: [‘django_filters.rest_framework.DjangoFilterBackend’],
    # ‘FILTER_REQUEST_PARAMETER’: ‘filter’, # customize filter parameter name
    # ‘SEARCH_PARAM’: ‘search’, # customize search parameter name
    # ‘ORDERING_PARAM’: ‘ordering’, # customize ordering parameter name
    }

    Add at the end for browsable API login/logout links

    LOGIN_URL = ‘/api-auth/login/’
    LOGOUT_URL = ‘/api-auth/logout/’
    3. **定义模型 (models.py):** (同上文 Book 模型)python

    my_app/models.py

    from django.db import models

    class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=100)
    published_date = models.DateField()
    isbn = models.CharField(max_length=13, unique=True)

    def __str__(self):
        return self.title
    

    4. **定义序列化器 (serializers.py):** (同上文 BookSerializer)python

    my_app/serializers.py

    from rest_framework import serializers
    from .models import Book

    class BookSerializer(serializers.ModelSerializer):
    class Meta:
    model = Book
    fields = [‘id’, ‘title’, ‘author’, ‘published_date’, ‘isbn’]
    5. **定义视图 (views.py):** (使用 ViewSet)python

    my_app/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):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    permission_classes = [IsAuthenticatedOrReadOnly] # 应用权限类
    6. **配置 URLs (my_app/urls.py):** (使用 Router)python

    my_app/urls.py

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

    from .views import CustomObtainAuthToken # 如果需要 token 认证视图

    router = DefaultRouter()
    router.register(r’books’, BookViewSet) # basename 可以省略,DRF 会推断

    urlpatterns = [
    path(”, include(router.urls)),
    # path(‘token-auth/’, CustomObtainAuthToken.as_view()), # token 认证 URL
    ]
    7. **配置项目 URLs (myapi/urls.py):**python

    myapi/urls.py

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

    from rest_framework.authtoken import views # 如果使用 DRF 内置的 ObtainAuthToken

    urlpatterns = [
    path(‘admin/’, admin.site.urls),
    path(‘api/’, include(‘my_app.urls’)), # 包含你的 API URLs
    # path(‘api-auth/’, include(‘rest_framework.urls’)), # 用于在 API 浏览器中显示登录/退出链接 (SessionAuthentication)
    # path(‘api-token-auth/’, views.obtain_auth_token) # 如果使用 DRF 内置的 ObtainAuthToken 视图
    ]
    8. **运行数据库迁移:**bash
    python manage.py makemigrations
    python manage.py migrate
    9. **创建超级用户 (如果需要测试认证/权限):**bash
    python manage.py createsuperuser
    10. **运行开发服务器:**bash
    python manage.py runserver
    “`

现在,你可以在浏览器中访问 http://127.0.0.1:8000/api/books/。由于你可能没有登录(默认使用了 SessionAuthentication 配合 browsable API),你只能看到图书列表(GET 请求),尝试 POST/PUT/DELETE 会被拒绝(403 Forbidden),因为你没有写权限。如果你在浏览器中登录了 Django 后台 (http://127.0.0.1:8000/admin/),然后再次访问 API 地址,你会发现你可以进行写操作了(因为 SessionAuthentication 认证了你)。

如果你配置了 TokenAuthentication 和 Token 获取视图,你可以使用 curl 或 Postman 等工具测试,在请求头部加上 Authorization: Token <你的token> 来进行认证。

11. 总结与后续学习方向

恭喜你!你已经从零开始了解并实践了 Django REST Framework 的核心概念:序列化器、视图(APIView, Generic Views, ViewSets)、URLs 和 Routers、Request/Response 对象,以及重要的安全特性——认证和权限。

这仅仅是 DRF 功能的冰山一角。作为下一步,你可以继续学习和探索:

  • 更高级的序列化器: 嵌套序列化器、定制字段、自定义验证。
  • 自定义权限和认证类: 实现更复杂的业务逻辑。
  • 使用过滤和搜索后台: 实现更强大的数据查询功能。
  • 配置分页: 根据你的需求调整分页样式和参数。
  • 限流策略: 保护你的 API 免受过载。
  • 异常处理: 定制 API 的错误响应。
  • 编写 API 测试: 确保你的 API 按预期工作。
  • 生成 API 文档: 使用 drf-yasg 等工具为你的 API 生成交互式文档。
  • 跨域资源共享 (CORS): 如果你的前端运行在不同的域名/端口,需要配置 CORS。
  • 不同的认证方式: JWT (JSON Web Tokens) 是另一种流行的 API 认证方式。

推荐学习资源:

  • DRF 官方文档 (必读): 它是最权威、最详细的资源。虽然初看可能觉得信息量大,但随着你对核心概念的理解加深,你会发现它非常有价值。www.django-rest-framework.org
  • Real Python DRF 教程: 提供了一系列高质量的 DRF 教程,非常适合进阶学习。
  • 在线课程: Udemy, Coursera, Bilibili 等平台有许多关于 Django 和 DRF 的课程。
  • GitHub 上的开源项目: 学习其他开发者如何使用 DRF 构建实际应用。

记住,最好的学习方式是实践。多动手写代码,尝试构建不同的 API,遇到问题时查阅文档和搜索解决方案。DRF 强大的功能和灵活性将极大地提升你构建 Web API 的效率和体验。祝你在 DRF 的学习旅程中一切顺利!


发表评论

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

滚动至顶部