GenericAPIView 是 Django REST Framework (DRF) 中的一个基础视图类,它继承自 APIView
,并添加了一些常用的功能,特别是与数据库模型交互的功能。它是 DRF 中通用视图和视图集的基础,提供了查询、序列化、分页等常用操作的标准实现。本质上它是 DRF 中所有通用视图(如 ListAPIView、RetrieveAPIView 等)的基础。
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer
class BookListView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request):
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
def post(self, request):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=201)
from django.urls import path
from .views import BookListView
urlpatterns = [
path('books/', BookListView.as_view(), name='book-list'),
]
GenericAPIView 继承了 APIView 的所有功能,因此可以通过 request
对象访问请求数据:
def post(self, request):
# 访问请求体数据
data = request.data
# 访问查询参数
query_params = request.query_params
# 访问用户信息
user = request.user
# 使用序列化器处理数据
serializer = self.get_serializer(data=data)
# ...
与 APIView 一样,使用 Response 对象返回响应:
from rest_framework.response import Response
from rest_framework import status
def get(self, request):
# ...
return Response(data, status=status.HTTP_200_OK)
queryset 属性:定义视图将操作的数据集
class BookView(GenericAPIView):
queryset = Book.objects.all() # 所有图书
get_queryset() 方法:允许动态定义查询集,比如基于当前用户过滤数据。
def get_queryset(self):
"""只返回当前用户的图书或公开图书"""
base_queryset = Book.objects.all()
# 未登录用户只能看到公开图书
if not self.request.user.is_authenticated:
return base_queryset.filter(is_public=True)
# 登录用户可以看到自己的图书和公开图书
return base_queryset.filter(
Q(owner=self.request.user) | Q(is_public=True)
)
何时使用:
queryset
属性:当查询集是固定的,不需要根据请求动态变化get_queryset()
方法:当需要根据请求用户、查询参数等动态调整查询集serializer_class 属性:指定用于序列化和反序列化的类。
class BookView(GenericAPIView):
serializer_class = BookSerializer
get_serializer_class() 方法:允许根据不同情况返回不同的序列化器类。
def get_serializer_class(self):
"""根据请求方法和用户角色返回不同的序列化器"""
# 管理员使用完整序列化器
if self.request.user.is_staff:
return BookAdminSerializer
# GET 请求使用详细序列化器
if self.request.method == 'GET':
return BookDetailSerializer
# POST/PUT 请求使用带验证的序列化器
return BookWriteSerializer
get_serializer() 方法:创建序列化器实例,处理常见参数如 many=True
。
# 在视图方法中使用
def get(self, request):
books = self.get_queryset()
serializer = self.get_serializer(books, many=True)
return Response(serializer.data)
# 自定义 get_serializer 方法
def get_serializer(self, *args, **kwargs):
"""添加额外上下文到序列化器"""
kwargs['context'] = self.get_serializer_context()
kwargs['context']['extra_data'] = self.get_extra_data()
return self.serializer_class(*args, **kwargs)
这两个属性控制如何从 URL 中获取单个对象。
lookup_field:模型中用于查找对象的字段名,默认为 'pk'。
lookup_url_kwarg:URL 中的参数名,默认与 lookup_field 相同。
class BookDetailView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
lookup_field = 'slug' # 使用 slug 字段查找
lookup_url_kwarg = 'book_slug' # URL 中的参数名
# URL 配置: path('books/<str:book_slug>/', BookDetailView.as_view())
def get(self, request, book_slug):
book = self.get_object() # 自动使用 book_slug 查找对象
serializer = self.get_serializer(book)
return Response(serializer.data)
控制如何对查询结果进行分页。
from rest_framework.pagination import PageNumberPagination
class CustomPagination(PageNumberPagination):
page_size = 20
page_size_query_param = 'size'
max_page_size = 100
class BookListView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
pagination_class = CustomPagination
def get(self, request):
queryset = self.filter_queryset(self.get_queryset())
# 执行分页
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
# 返回分页响应(包含分页链接等信息)
return self.get_paginated_response(serializer.data)
# 如果未启用分页,返回所有结果
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
分页响应格式:
{
"count": 100,
"next": "http://api.example.org/books/?page=2",
"previous": null,
"results": [
// 当前页的数据
]
}
控制如何过滤查询集。
from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackend
class BookListView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
# 配置过滤后端
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
# DjangoFilterBackend 配置
filterset_fields = ['category', 'author', 'published_year']
# SearchFilter 配置
search_fields = ['title', 'description', 'author__name']
# OrderingFilter 配置
ordering_fields = ['title', 'published_date', 'rating']
ordering = ['-published_date'] # 默认排序
def get(self, request):
# filter_queryset 会应用所有配置的过滤器
queryset = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
使用示例:
此方法用于获取单个对象,并自动处理权限检查和 404 错误。
def get_object(self):
"""
获取对象并进行自定义处理
"""
# 获取查询集
queryset = self.filter_queryset(self.get_queryset())
# 获取查找参数
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(queryset, **filter_kwargs)
# 检查对象权限
self.check_object_permissions(self.request, obj)
# 记录访问日志
self.log_object_access(obj)
return obj
def log_object_access(self, obj):
"""记录对象访问日志"""
AccessLog.objects.create(
user=self.request.user,
object_id=obj.id,
object_type=obj.__class__.__name__
)
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import status
class ArticleView(GenericAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""根据用户角色返回不同的查询集"""
user = self.request.user
if user.is_staff:
return Article.objects.all()
return Article.objects.filter(status='published')
def get(self, request):
"""获取文章列表"""
articles = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(articles)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(articles, many=True)
return Response(serializer.data)
def post(self, request):
"""创建新文章"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
# 添加作者信息
serializer.validated_data['author'] = request.user
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
class StandardResponse(GenericAPIView):
"""提供标准响应格式的基类"""
def get_standard_response(self, data=None, message="", code=0, status=status.HTTP_200_OK, **kwargs):
"""生成标准响应格式"""
response_data = {
"code": code,
"message": message,
"data": data or {},
}
# 添加额外数据
response_data.update(kwargs)
return Response(response_data, status=status)
class ProductListView(StandardResponse):
queryset = Product.objects.all()
serializer_class = ProductSerializer
def get(self, request):
products = self.get_queryset()
serializer = self.get_serializer(products, many=True)
# 使用标准响应格式
return self.get_standard_response(
data=serializer.data,
message="获取产品列表成功",
total_count=products.count()
)
from django.db.models import Count, Avg, Sum
class SalesAnalyticsView(GenericAPIView):
queryset = Order.objects.all()
serializer_class = OrderSerializer
def get(self, request):
# 获取时间范围参数
start_date = request.query_params.get('start_date')
end_date = request.query_params.get('end_date')
# 构建基础查询集
queryset = self.get_queryset()
if start_date:
queryset = queryset.filter(created_at__gte=start_date)
if end_date:
queryset = queryset.filter(created_at__lte=end_date)
# 执行聚合查询
analytics = queryset.aggregate(
total_sales=Sum('total_amount'),
average_order_value=Avg('total_amount'),
order_count=Count('id')
)
# 按产品分组统计
product_stats = queryset.values('product__name').annotate(
sales=Sum('total_amount'),
quantity=Sum('quantity')
).order_by('-sales')
# 构建响应
response_data = {
'summary': analytics,
'product_stats': product_stats
}
return Response(response_data)
GenericAPIView 本身不提供 CRUD 操作的实现,但 DRF 提供了一系列 Mixin 类,可以与 GenericAPIView 组合使用:
list()
方法,实现列表查询retrieve()
方法,实现获取单个对象update()
和 partial_update()
方法,实现更新对象destroy()
方法,实现删除对象from rest_framework.mixins import ListModelMixin, CreateModelMixin
from rest_framework.generics import GenericAPIView
class BookListCreateView(ListModelMixin, CreateModelMixin, GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
class DynamicFilterBookView(GenericAPIView):
queryset = Book.objects.all() # 设置基础查询集为所有图书
serializer_class = BookSerializer # 设置序列化器
def filter_queryset(self, queryset):
# 首先调用父类的 filter_queryset 方法
# 这会应用配置的 filter_backends(如果有)
queryset = super().filter_queryset(queryset)
# 遍历所有查询参数
for param, value in self.request.query_params.items():
# 排除分页参数
if param not in ['page', 'page_size'] and hasattr(Book, param):
# 检查参数名是否是 Book 模型的属性
filter_kwargs = {param: value}
# 应用过滤条件
queryset = queryset.filter(**filter_kwargs)
return queryset
def get(self, request):
# 获取过滤后的查询集
queryset = self.filter_queryset(self.get_queryset())
# 序列化数据(注意 many=True 表示序列化多个对象)
serializer = self.get_serializer(queryset, many=True)
# 返回响应
return Response(serializer.data)
get
方法get
方法首先调用 self.get_queryset()
获取基础查询集self.filter_queryset()
应用过滤filter_queryset
中,首先调用父类方法应用配置的过滤器假设 Book 模型有 title、author、genre
和 published_year
字段,用户可以这样使用 API:
/api/books/?title=Django
- 过滤标题包含 "Django" 的图书/api/books/?author=Martin&genre=Fantasy
- 过滤作者为 "Martin" 且类型为 "Fantasy" 的图书/api/books/?published_year=2022
- 过滤 2022 年出版的图书from rest_framework.permissions import IsAuthenticated
from rest_framework.exceptions import PermissionDenied
class ProtectedBookView(GenericAPIView):
queryset = Book.objects.all()
serializer_class = BookSerializer
permission_classes = [IsAuthenticated]
def check_permissions(self, request):
"""添加自定义权限检查"""
super().check_permissions(request)
# 自定义权限逻辑
if not request.user.is_staff and request.method != 'GET':
raise PermissionDenied("只有管理员可以修改数据")
def get(self, request):
# ...
# views.py
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
# 自定义分页类
class StandardResultsSetPagination(PageNumberPagination):
page_size = 10 # 每页显示数量
page_size_query_param = 'page_size' # 允许客户端通过此参数控制每页大小
max_page_size = 100 # 每页最大显示数量
class UserListView(GenericAPIView):
pagination_class = StandardResultsSetPagination
def get(self, request):
users = User.objects.all()
# 获取分页器实例
paginator = self.pagination_class()
# 对查询集进行分页
paginated_users = paginator.paginate_queryset(users, request)
# 序列化分页后的数据
serializer = UserSerializer(paginated_users, many=True)
# 返回带分页信息的响应
return paginator.get_paginated_response(serializer.data)
# 客户端请求示例: /api/users/?page=2
class PageNumberPaginationView(GenericAPIView):
def get(self, request):
paginator = PageNumberPagination()
paginator.page_size = 10
users = User.objects.all()
result_page = paginator.paginate_queryset(users, request)
serializer = UserSerializer(result_page, many=True)
return paginator.get_paginated_response(serializer.data)
# 客户端请求示例: /api/users/?limit=10&offset=20
from rest_framework.pagination import LimitOffsetPagination
class LimitOffsetPaginationView(GenericAPIView):
def get(self, request):
paginator = LimitOffsetPagination()
users = User.objects.all()
result_page = paginator.paginate_queryset(users, request)
serializer = UserSerializer(result_page, many=True)
return paginator.get_paginated_response(serializer.data)
适用于大型数据集和实时数据流,基于"游标"而非页码:
# 客户端请求示例: /api/users/?cursor=cD0yMDIwLTAxLTAxKzAwJTNBMDAlM0EwMA==
from rest_framework.pagination import CursorPagination
class MyCursorPagination(CursorPagination):
ordering = '-created_at' # 排序字段
page_size = 10
class CursorPaginationView(GenericAPIView):
def get(self, request):
paginator = MyCursorPagination()
users = User.objects.all()
result_page = paginator.paginate_queryset(users, request)
serializer = UserSerializer(result_page, many=True)
return paginator.get_paginated_response(serializer.data)
在settings.py
中可以全局配置分页:
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
}
class CustomPagination(PageNumberPagination):
def get_paginated_response(self, data):
return Response({
'links': {
'next': self.get_next_link(),
'previous': self.get_previous_link()
},
'count': self.page.paginator.count,
'total_pages': self.page.paginator.num_pages,
'current_page': self.page.number,
'results': data
})
GenericAPIView 是 DRF 中非常强大的基础视图类,它提供了与数据库模型和序列化器交互的通用功能,包括:
通过合理使用这些功能,可以大大简化 API 开发工作,提高代码的可维护性和可读性。同时,GenericAPIView 也是 DRF 中更高级视图(如 ListAPIView、RetrieveAPIView 等)的基础。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。