未命名
一、接口开发方式分类
1. 函数视图(Function-Based Views)
from rest_framework.decorators import api_view
from rest_framework.response import Response
@api_view(['GET'])
def book_list(request):
"""
基于函数的书籍列表视图
"""
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
return Response(serializer.data)
@api_view(['POST'])
def create_book(request):
"""
基于函数的书籍创建视图
"""
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
return Response(serializer.errors, status=400)
特点:
简单直接的装饰器语法
适合小型或单一操作的端点
缺乏面向对象的组织结构
2. 基于类的视图(Class-Based Views)
from rest_framework.views import APIView
class BookList(APIView):
"""
基于APIView的书籍列表视图
"""
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=201)
return Response(serializer.errors, status=400)
特点:
将HTTP方法分离为独立的方法
提供更好的代码组织和复用性
可以重写get、post、put、patch、delete等方法
3. 通用视图(Generic Views)
from rest_framework import generics
class BookListCreateView(generics.ListCreateAPIView):
"""
使用通用视图处理书籍列表和创建
"""
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [permissions.IsAuthenticated]
class BookRetrieveUpdateDestroyView(generics.RetrieveUpdateDestroyAPIView):
"""
处理书籍详情、更新和删除
"""
queryset = Book.objects.all()
serializer_class = BookSerializer
lookup_field = 'isbn' # 自定义查找字段
DRF通用视图类型:
视图类
功能
HTTP方法
ListAPIView
资源列表
GET
CreateAPIView
创建资源
POST
RetrieveAPIView
获取详情
GET
UpdateAPIView
更新资源
PUT/PATCH
DestroyAPIView
删除资源
DELETE
ListCreateAPIView
列表+创建
GET, POST
RetrieveUpdateAPIView
详情+更新
GET, PUT, PATCH
RetrieveDestroyAPIView
详情+删除
GET, DELETE
RetrieveUpdateDestroyAPIView
详情+更新+删除
GET, PUT, PATCH, DELETE
4. 视图集(ViewSets)
from rest_framework import viewsets
class BookViewSet(viewsets.ModelViewSet):
"""
使用视图集处理所有书籍操作
"""
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [IsAdminOrReadOnly]
def get_queryset(self):
"""根据参数过滤查询集"""
author_id = self.request.query_params.get('author_id')
if author_id:
return Book.objects.filter(author_id=author_id)
return super().get_queryset()
@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
"""发布书籍的自定义操作"""
book = self.get_object()
book.publish()
return Response({'status': 'published'})
视图集类型:
视图集类
描述
包含操作
ViewSet
基础视图集
无默认操作
GenericViewSet
通用视图集
核心通用功能
ModelViewSet
完整CRUD操作
list, create, retrieve, update, partial_update, destroy
ReadOnlyModelViewSet
只读操作
list, retrieve
二、路由
1. FBV/CBV/GV
urlpatterns = [
path('fbv/list/', views.course_detail,name='fbv-list'),
path('fbv/list2/<int:pk>/', views.course_detail2,name='fbv-list2'),
# 类视图要使用as_view():
# as_view()是类视图的入口方法,将类转换为Django可调用的视图函数
path('cbv/list/',views.CourseListView.as_view(),name='cbv-list'),
path('gcbv/list/',views.GCourseList.as_view(),name='gcbv-list'),
path('gcbv/detail/<int:pk>',views.GCourseDetail.as_view(),name='gcbv-list'),
]
2. VS
在 Django REST Framework (DRF) 中,路由器是构建 RESTful API 的关键组件,它自动为视图集生成 URL 配置。
路由器类
描述
特点
SimpleRouter
基本路由器
标准CRUD路由
DefaultRouter
默认路由器
包含根视图和可选格式后缀
CustomRouter
自定义路由器
扩展或自定义路由行为
本小节的例子均以****有一个 BookViewSet 视图集,包含标准 CRUD 操作和一个自定义动作 mark_as_read为前提。
# register(prefix, viewset, basename=None)
# prefix不能带/
router.register(r'books', BookViewSet, basename='book')
1. prefix**(必需参数,字符串)**
作用:用于生成URL前缀。
示例:'users',那么生成的URL将类似于/users/(列表视图)和/users/{pk}/(详情视图)。
注意:这个前缀通常是资源名称的复数形式,如'books'、'authors'。
2. viewset**(必需参数,视图集类)**
作用:要注册的视图集类(必须是ViewSet的子类,如ModelViewSet、ReadOnlyModelViewSet或自定义的ViewSet)。
示例:UserViewSet。
3. basename**(可选参数,字符串)**
作用:生成的URL模式名称的前缀(用于reverse反向解析)。
默认值:如果没有提供,将根据queryset属性(如果视图集有设置)或模型名(小写)自动生成。如果自动生成失败,则必须显式提供。
生成规则:自动生成时,使用模型名称的小写形式(如果视图集有queryset属性)。例如,如果queryset是User.objects.all(),那么basename将是'user'(但注意,生成的URL名称会是user-list和user-detail,这里的basename是'user')。
何时需要显式指定:如果视图集没有设置queryset属性,或者你想覆盖默认的名称,则需要提供此参数。
示例:basename='user',那么生成的URL名称将是user-list、user-detail等。
特别说明:basename的自动生成规则
如果视图集设置了queryset属性,则basename = queryset.model._meta.model_name(模型名称的小写,单数形式)。
如果没有设置queryset,必须显式指定basename,否则会抛出ImproperlyConfigured异常。
1. SimpleRouter(基本路由器)
核心特点
最轻量级的路由器实现
只生成基本的 CRUD 路由
不包含额外的元数据端点
适合简单 API 或性能敏感场景
路由规则
为每个视图集生成两条基本路由:
列表路由:处理集合操作(list/create)
详情路由:处理单个对象操作(retrieve/update/delete)
不支持格式后缀(如 .json)
不包含 API 根视图
from rest_framework.routers import SimpleRouter
from .views import BookViewSet
from django.urls import path,include
router = SimpleRouter()
router.register(r'books', BookViewSet, basename='book')
urlpatterns = [
path("",include(router.urls)),
]
'''
# 列表路由
^books/$
- GET: book-list (列表)
- POST: book-create (创建)
# 详情路由
^books/(?P<pk>[^/.]+)/$
- GET: book-retrieve (详情)
- PUT: book-update (全量更新)
- PATCH: book-partial-update (部分更新)
- DELETE: book-destroy (删除)
# 自定义动作 (detail=True)
^books/(?P<pk>[^/.]+)/mark_as_read/$
- POST: book-mark-as-read (自定义动作)
# 实际URL示例
http://api.example.com/books/
http://api.example.com/books/1/
http://api.example.com/books/1/mark_as_read/
'''
2. DefaultRouter(默认路由器)
核心特点
DRF 的默认路由器
包含 API 根视图(列出所有端点)
支持格式后缀(.json, .api)
提供超链接 API 支持
包含额外的元数据
路由规则
包含 SimpleRouter 的所有功能
额外添加 API 根视图
支持格式后缀(.json, .api 等)
为每个路由生成两个版本(带后缀和不带后缀)
包含超链接关系支持
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
from django.urls import path,include
router = SimpleRouter()
router.register(r'books', BookViewSet, basename='book')
urlpatterns = [
path("",include(router.urls)),
]
'''
# API 根视图
^$
- GET: api-root (列出所有端点)
# 书籍资源
^books/$
- GET: book-list
- POST: book-create
^books\.(?P<format>[a-z0-9]+)/?$
- GET: book-list (带格式后缀)
^books/(?P<pk>[^/.]+)/$
- GET: book-retrieve
- PUT: book-update
- PATCH: book-partial-update
- DELETE: book-destroy
^books/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$
- GET: book-retrieve (带格式后缀)
^books/(?P<pk>[^/.]+)/mark_as_read/$
- POST: book-mark-as-read
^books/(?P<pk>[^/.]+)/mark_as_read\.(?P<format>[a-z0-9]+)/?$
- POST: book-mark-as-read (带格式后缀)
# 实际URL示例
http://api.example.com/ # API根视图
http://api.example.com/books.json
http://api.example.com/books/1.api
http://api.example.com/authors/
http://api.example.com/authors/1/mark_as_read.json
'''
3. CustomRouter(自定义路由器)
from rest_framework.routers import DefaultRouter, Route
from rest_framework.response import Response
from rest_framework.views import APIView
class StatsView(APIView):
"""自定义统计端点"""
def get(self, request):
return Response({"total_books": 1000, "active_books": 750})
class CustomAPIRootView(APIView):
"""自定义API根视图"""
def get(self, request):
return Response({
"message": "欢迎使用图书API",
"endpoints": {
"books": reverse('book-list', request=request),
"authors": reverse('author-list', request=request),
"stats": reverse('api-stats', request=request)
}
})
class CustomBookRouter(DefaultRouter):
routes = [
# 标准列表路由
Route(
url=r'^books/$',
mapping={'get': 'list', 'post': 'create'},
name='book-list',
detail=False,
initkwargs={'suffix': 'List'}
),
# 标准详情路由
Route(
url=r'^books/(?P<pk>[^/.]+)/$',
mapping={
'get': 'retrieve',
'put': 'update',
'patch': 'partial_update',
'delete': 'destroy'
},
name='book-detail',
detail=True,
initkwargs={'suffix': 'Detail'}
),
# 自定义批量操作
Route(
url=r'^books/bulk/$',
mapping={'post': 'bulk_create', 'delete': 'bulk_delete'},
name='book-bulk',
detail=False,
initkwargs={}
),
# 自定义归档操作
Route(
url=r'^books/(?P<pk>[^/.]+)/archive/$',
mapping={'post': 'archive'},
name='book-archive',
detail=True,
initkwargs={}
)
]
def get_api_root_view(self, api_urls=None):
return CustomAPIRootView.as_view()
def get_urls(self):
urls = super().get_urls()
# 添加统计端点
urls.append(
path('stats/', StatsView.as_view(), name='api-stats')
)
return urls
# 使用
router = CustomBookRouter()
router.register(r'', BookViewSet) # 使用空前缀
# 生成路由:
# /books/ - 列表/创建
# /books/bulk/ - 批量操作
# /books/{pk}/ - 详情/更新
# /books/{pk}/archive/ - 归档
# /stats/ - 统计端点
三、ViewSet
在Django REST framework中,ViewSet是一个用于处理一组相关视图的类。它通过提供诸如list, create, retrieve, update, partial_update, destroy等动作(actions)来封装常见的CRUD操作。使用ViewSet可以简化代码,并且与路由器的配合使得URL配置更加简洁。
ViewSet类本身并不提供任何动作(action),而是提供了一些基础方法(如get_object, get_queryset)和可以重写的动作方法。
1. 配置
1. 核心配置
1. queryset
作用:定义视图集操作的数据源
类型:QuerySet
默认值:None
示例:
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
2. serializer_class
作用:指定默认序列化器
类型:Serializer 类
默认值:None
示例:
class BookViewSet(viewsets.ModelViewSet):
serializer_class = BookSerializer
3. permission_classes
作用:设置访问权限控制
类型:权限类列表
默认值:[permissions.AllowAny]
示例:
from rest_framework import permissions
class BookViewSet(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated]
4. authentication_classes
作用:指定认证类
类型:认证类列表
默认值:[SessionAuthentication, BasicAuthentication]
示例:
from rest_framework.authentication import TokenAuthentication
class BookViewSet(viewsets.ModelViewSet):
authentication_classes = [TokenAuthentication]
2. 行为控制配置
1. lookup_field
作用:指定对象查找字段(默认为 'pk')
类型:字符串
默认值:'pk'
示例:
class BookViewSet(viewsets.ModelViewSet):
lookup_field = 'isbn'
2. lookup_url_kwarg
指定 URL 中的查找字段:
class AuthorSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Author
fields = ['url', 'name']
extra_kwargs = {
'url': {
'view_name': 'author-detail',
'lookup_field': 'uuid' # 使用UUID而非ID
}
}
3. pagination_class
作用:指定分页类
类型:分页类
默认值:None
示例:
from rest_framework.pagination import PageNumberPagination
class CustomPagination(PageNumberPagination):
page_size = 20
class BookViewSet(viewsets.ModelViewSet):
pagination_class = CustomPagination
4. ordering_fields
作用:指定可排序字段
类型:字符串列表
默认值:None
示例:
class BookViewSet(viewsets.ModelViewSet):
ordering_fields = ['title', 'publication_date', 'rating']
5. search_fields
作用:指定可搜索字段
类型:字符串列表
默认值:None
示例:
class BookViewSet(viewsets.ModelViewSet):
search_fields = ['title', 'author__name', 'description']
2. 自定义动作
如果你需要添加自定义动作(即非标准动作),可以使用@action装饰器。自定义动作可以响应GET、POST等请求。
from rest_framework.decorators import action
from rest_framework.response import Response
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 自定义动作,处理GET请求
@action(detail=True, methods=['get'])
def recent(self, request, pk=None):
book = self.get_object()
# 假设我们有一个方法返回最近出版的书籍(这里仅做示例)
recent_books = Book.objects.filter(published_date__gte='2023-01-01')
serializer = self.get_serializer(recent_books, many=True)
return Response(serializer.data)
# 另一个自定义动作,处理POST请求
@action(detail=False, methods=['post'])
def bulk_create(self, request):
# 处理批量创建
serializer = self.get_serializer(data=request.data, many=True)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
return Response(serializer.data, status=201)
@action装饰器参数说明:
detail: 布尔值。如果为True,表示这个动作是针对单个对象(即需要pk),如果为False,则针对整个集合。
detail=True: 对应URL如 /{prefix}/{lookup}/action_name/
detail=False: 对应URL如 /{prefix}/action_name/
methods: 列表,指定这个动作响应的HTTP方法,如['get', 'post']。
url_path: 自定义URL路径,默认使用函数名。
url_name: 为URL命名,用于反向解析。
参数
类型
默认值
说明
必需
detail
bool
True
指定是详情操作还是集合操作
是
methods
list
['get']
支持的 HTTP 方法列表
否
url_path
str
方法名
URL 路径后缀
否
url_name
str
方法名
路由名称后缀
否
permission_classes
list
None
专属权限类
否
authentication_classes
list
None
专属认证类
否
throttle_classes
list
None
专属限流类
否
serializer_class
class
None
专属序列化器
否
pagination_class
class
None
专属分页类
否
filter_backends
list
None
专属过滤后端
否
kwargs
dict
{}
额外视图参数
否
3. 钩子函数
perform_create(), perform_update() 和 perform_destroy() 是三个关键的操作钩子方法,它们提供了在核心操作执行前后添加自定义逻辑的能力。
1. perform_create(serializer)
在创建新对象时,在对象保存到数据库之前或之后执行自定义逻辑。
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
def perform_create(self, serializer):
# 自动设置作者和创建时间
serializer.save(
author=self.request.user,
created_at=timezone.now()
)
# 发送通知
book = serializer.instance
send_email_notification(
subject=f"新书创建: {book.title}",
message=f"书籍 '{book.title}' 已成功创建",
recipients=['admin@example.com']
)
在 create() 方法验证数据成功后,实际保存对象到数据库之前。
2. perform_update(serializer)
在更新现有对象时,在对象保存到数据库之前或之后执行自定义逻辑。
class OrderViewSet(viewsets.ModelViewSet):
queryset = Order.objects.all()
serializer_class = OrderSerializer
def perform_update(self, serializer):
# 获取原始对象
original_order = self.get_object()
# 保存更新
updated_order = serializer.save(updated_at=timezone.now())
# 记录变更历史
OrderHistory.objects.create(
order=updated_order,
user=self.request.user,
changes=calculate_changes(original_order, updated_order)
)
# 状态变更通知
if original_order.status != updated_order.status:
send_status_change_notification(updated_order)
在 update() 或 partial_update() 方法验证数据成功后,实际保存更新到数据库之前。
3. perform_destroy(instance)
在删除对象时,在实际从数据库删除之前或之后执行自定义逻辑。
class UserProfileViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
def perform_destroy(self, instance):
# 软删除用户资料
instance.is_active = False
instance.deactivated_at = timezone.now()
instance.deactivated_by = self.request.user
instance.save()
# 记录删除操作
DeactivationLog.objects.create(
user=instance.user,
deactivated_by=self.request.user,
reason="用户请求删除"
)
# 通知用户
send_deactivation_email(instance.user)
在 destroy() 方法确定对象存在后,实际执行删除操作之前。
4. 分页器
DRF(Django REST Framework)中的分页器(Pagination)用于控制分页行为,即在返回大量数据时,将数据拆分为多个页面。分页可以提升性能并减少单个响应的负载,同时提升用户体验。
DRF提供了三种内置的分页器:
PageNumberPagination:基于页码的分页,如 ?page=2
LimitOffsetPagination:基于偏移的分页,如 ?limit=20&offset=40
CursorPagination:基于游标的分页,提供更安全的分页方式(不暴露页码),适合大型数据集和实时更新的数据流
分页器可以在全局配置
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 10, # 每页大小
}
也可以在视图中进行设置
from rest_framework.pagination import PageNumberPagination
from rest_framework import viewsets
class LargeResultsSetPagination(PageNumberPagination):
page_size = 20
page_size_query_param = 'page_size'
max_page_size = 100
class MyViewSet(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MySerializer
pagination_class = LargeResultsSetPagination # 使用自定义分页
1. PageNumberPagination
使用页码(如page=2),通常需要配合page_size(每页大小)使用。
参数:
page_query_param:页码参数名(默认为'page')
page_size_query_param:每页大小参数名(默认为None,表示不可由客户端设置)
max_page_size:每页最大大小(如果允许客户端设置)
page_size:默认每页大小
django_paginator_class:使用的Django Paginator类
自定义示例:
class StandardPageNumberPagination(PageNumberPagination):
page_size = 10 # 默认每页数量
page_query_param = 'page' # 页码参数名称
page_size_query_param = 'page_size' # 每页数量参数名称
max_page_size = 50 # 客户端最多允许设置的每页数量
# 请求示例
GET /api/books/?page=3&page_size=20
# 响应
{
"count": 1000,
"next": "http://api.example.com/books/?page=4",
"previous": "http://api.example.com/books/?page=2",
"results": [
// 当前页数据
]
}
2. LimitOffsetPagination
基于数据库的LIMIT和OFFSET。参数为limit(限制数量)和offset(偏移量)。
参数:
limit_query_param:每页大小参数名(默认'limit')
offset_query_param:偏移量参数名(默认'offset')
max_limit:最大每页大小
自定义示例:
class CustomLimitOffsetPagination(LimitOffsetPagination):
default_limit = 10 # 默认每页数量
limit_query_param = 'limit' # 每页数量参数名称
offset_query_param = 'offset' # 偏移量参数名称
max_limit = 100 # 最大允许的每页数量
3. CursorPagination
基于游标的分页,返回一个包含下一页游标的结果。这种分页方式在无序数据中表现更好,并且可以避免页码分页的数据缺失或重复问题(如在分页过程中有新增或删除)。
参数:
cursor_query_param:游标参数名(默认'cursor')
page_size:每页大小
ordering:分页使用的排序字段(通常是创建时间或主键)
page_size_query_param:每页大小参数名(可选)
自定义示例:
class CustomCursorPagination(CursorPagination):
page_size = 10 # 默认每页数量
cursor_query_param = 'cursor' # 游标参数名称
ordering = '-created' # 按创建时间降序
4. 示例
from rest_framework import generics
from rest_framework.pagination import PageNumberPagination
class MyPaginator(PageNumberPagination):
page_size = 5
page_size_query_param = 'page_size'
max_page_size = 100
class MyListView(generics.ListAPIView):
queryset = MyModel.objects.all()
serializer_class = MySerializer
pagination_class = MyPaginator
分页后的响应结构通常包含:
count:总记录数
next:下一页的URL(如果没有则为null)
previous:上一页的URL(如果没有则为null)
results:当前页的数据列表
{
"count": 100,
"next": "https://api.example.com/mymodel/?page=2&page_size=5",
"previous": null,
"results": [
// 当前页的数据
]
}
分页只用于GET请求,即用于列表接口。
使用分页时,需要权衡性能和用户体验。例如,CursorPagination在大数据集上性能更好,但不提供页码导航。
全局设置的分页器可以被视图级别的设置覆盖。
如果不希望某个视图分页,可以在视图中设置pagination_class = None。
5. 自定义分页响应
可以重写分页器的get_paginated_response方法来自定义响应结构:
class CustomPageNumberPagination(PageNumberPagination):
def get_paginated_response(self, data):
return Response({
'total_pages': self.page.paginator.num_pages,
'current_page': self.page.number,
'next_page': self.get_next_link(),
'prev_page': self.get_previous_link(),
'data': data
})
5. 自定义数据集获取
在 Django REST Framework (DRF) 中,get_queryset 和 get_object 是两个核心方法,它们控制着视图如何访问数据。
1. get_queryset 方法详解
get_queryset 决定视图返回的查询集(QuerySet)。默认行为是返回 self.queryset 属性指定的模型查询集:
class BookListView(generics.ListAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 默认相当于
def get_queryset(self):
return self.queryset.all()
示例:
def get_queryset(self):
user = self.request.user
# 管理员可以查看所有数据
if user.is_superuser:
return super().get_queryset()
# 普通用户只能查看自己的数据
return super().get_queryset().filter(created_by=user)
2. get_object 方法详解
get_object 用于在详情视图中检索单个对象。默认行为:
class BookDetailView(generics.RetrieveAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 默认相当于
def get_object(self):
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
obj = get_object_or_404(self.get_queryset(), **filter_kwargs)
self.check_object_permissions(self.request, obj)
return obj
示例:
class EmployeeDataView(OrganizationModelViewMixin, generics.RetrieveAPIView):
serializer_class = EmployeeSerializer
def get_queryset(self):
queryset = super().get_queryset()
user = self.request.user
# HR可以查看所有员工,经理只能查看自己团队的员工
if user.role == 'HR':
return queryset
elif user.role == 'MANAGER':
return queryset.filter(team=user.team)
else:
return queryset.none() # 普通员工无访问权限
def get_object(self):
obj = super().get_object()
# 额外检查:管理员可以查看敏感信息
if not self.request.user.is_hr and obj.has_sensitive_info:
# 移除敏感字段
obj.sensitive_info = None
return obj
最后更新于