从入门到精通Django REST Framework-(五)

news/2025/3/1 0:11:05/文章来源:https://www.cnblogs.com/rxg456/p/18744374

一. 什么是 GenericAPIView?

GenericAPIView 是 Django REST Framework (DRF) 中的一个基础视图类,它继承自 APIView,并添加了一些常用的功能,特别是与数据库模型交互的功能。它是 DRF 中通用视图和视图集的基础,提供了查询、序列化、分页等常用操作的标准实现。本质上它是 DRF 中所有通用视图(如 ListAPIView、RetrieveAPIView 等)的基础。

二. 为什么要使用 GenericAPIView?

  • 减少重复代码 - 提供了常见操作的标准实现,如获取查询集、序列化数据等
  • 提高开发效率 - 内置了分页、过滤、排序等功能
  • 代码组织更清晰 - 将通用逻辑与业务逻辑分离
  • 易于扩展 - 可以通过重写方法来自定义行为
  • 与 DRF 生态系统集成 - 与 DRF 的其他组件(如序列化器、权限等)无缝协作

三. 基础用法

创建视图类

from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializerclass BookListView(GenericAPIView):queryset = Book.objects.all()serializer_class = BookSerializerdef 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)

配置 URL 路由

from django.urls import path
from .views import BookListViewurlpatterns = [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 statusdef get(self, request):# ...return Response(data, status=status.HTTP_200_OK)

GenericAPIView核心概念详解

1. 查询集(queryset)与 get_queryset()

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() 方法:当需要根据请求用户、查询参数等动态调整查询集

2. 序列化器(serializer_class)与 get_serializer()

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)

3. 对象查找(lookup_field 和 lookup_url_kwarg)

这两个属性控制如何从 URL 中获取单个对象。

lookup_field:模型中用于查找对象的字段名,默认为 'pk'。

lookup_url_kwarg:URL 中的参数名,默认与 lookup_field 相同。

class BookDetailView(GenericAPIView):queryset = Book.objects.all()serializer_class = BookSerializerlookup_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)

4. 分页(pagination_class)

控制如何对查询结果进行分页。

from rest_framework.pagination import PageNumberPaginationclass CustomPagination(PageNumberPagination):page_size = 20page_size_query_param = 'size'max_page_size = 100class BookListView(GenericAPIView):queryset = Book.objects.all()serializer_class = BookSerializerpagination_class = CustomPaginationdef 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": [// 当前页的数据]
}

5. 过滤(filter_backends)

控制如何过滤查询集。

from rest_framework.filters import SearchFilter, OrderingFilter
from django_filters.rest_framework import DjangoFilterBackendclass 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)

使用示例:

  • 精确过滤:/books/?category=fiction&author=1
  • 搜索:/books/?search=django
  • 排序:/books/?ordering=-rating,title

6. get_object() 方法详解

此方法用于获取单个对象,并自动处理权限检查和 404 错误。

def get_object(self):"""获取对象并进行自定义处理"""# 获取查询集queryset = self.filter_queryset(self.get_queryset())# 获取查找参数lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_fieldfilter_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 objdef log_object_access(self, obj):"""记录对象访问日志"""AccessLog.objects.create(user=self.request.user,object_id=obj.id,object_type=obj.__class__.__name__)

实际应用场景示例

场景 1: 带权限控制的 API

from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import statusclass ArticleView(GenericAPIView):queryset = Article.objects.all()serializer_class = ArticleSerializerpermission_classes = [IsAuthenticated]def get_queryset(self):"""根据用户角色返回不同的查询集"""user = self.request.userif 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.userserializer.save()return Response(serializer.data, status=status.HTTP_201_CREATED)

场景 2: 自定义响应格式

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 = ProductSerializerdef 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())

场景 3: 复杂查询和聚合

from django.db.models import Count, Avg, Sumclass SalesAnalyticsView(GenericAPIView):queryset = Order.objects.all()serializer_class = OrderSerializerdef 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)

五. 与 Mixin 类的关系

GenericAPIView 本身不提供 CRUD 操作的实现,但 DRF 提供了一系列 Mixin 类,可以与 GenericAPIView 组合使用:

  • ListModelMixin: 提供 list() 方法,实现列表查询
  • CreateModelMixin: 提供 create() 方法,实现创建对象
  • RetrieveModelMixin: 提供 retrieve() 方法,实现获取单个对象
  • UpdateModelMixin: 提供 update()partial_update() 方法,实现更新对象
  • DestroyModelMixin: 提供 destroy() 方法,实现删除对象
from rest_framework.mixins import ListModelMixin, CreateModelMixin
from rest_framework.generics import GenericAPIViewclass BookListCreateView(ListModelMixin, CreateModelMixin, GenericAPIView):queryset = Book.objects.all()serializer_class = BookSerializerdef get(self, request, *args, **kwargs):return self.list(request, *args, **kwargs)def post(self, request, *args, **kwargs):return self.create(request, *args, **kwargs)

六. 其他技巧

1. 使用动态查询参数

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 querysetdef get(self, request):# 获取过滤后的查询集queryset = self.filter_queryset(self.get_queryset())# 序列化数据(注意 many=True 表示序列化多个对象)serializer = self.get_serializer(queryset, many=True)# 返回响应return Response(serializer.data)
工作原理
  1. 当收到 GET 请求时,视图调用 get 方法
  2. get 方法首先调用 self.get_queryset() 获取基础查询集
  3. 然后调用 self.filter_queryset() 应用过滤
  4. filter_queryset 中,首先调用父类方法应用配置的过滤器
  5. 然后遍历所有查询参数,检查是否与模型字段匹配
  6. 对于匹配的参数,构建过滤条件并应用到查询集
  7. 最后序列化过滤后的查询集并返回响应
使用示例

假设 Book 模型有 title、author、genrepublished_year 字段,用户可以这样使用 API:

  • /api/books/?title=Django - 过滤标题包含 "Django" 的图书
  • /api/books/?author=Martin&genre=Fantasy - 过滤作者为 "Martin" 且类型为 "Fantasy" 的图书
  • /api/books/?published_year=2022 - 过滤 2022 年出版的图书
优点
  • 灵活性 - 无需为每个过滤条件编写专门的代码
  • 可扩展性 - 添加新的模型字段后,自动支持对该字段的过滤
  • 简洁性 - 代码简洁明了,易于维护

2. 添加自定义权限检查

from rest_framework.permissions import IsAuthenticated
from rest_framework.exceptions import PermissionDeniedclass ProtectedBookView(GenericAPIView):queryset = Book.objects.all()serializer_class = BookSerializerpermission_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):# ...

3."全面"分页

列表页分页

# 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 = StandardResultsSetPaginationdef 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)

页码分页 (PageNumberPagination)

# 客户端请求示例: /api/users/?page=2
class PageNumberPaginationView(GenericAPIView):def get(self, request):paginator = PageNumberPagination()paginator.page_size = 10users = User.objects.all()result_page = paginator.paginate_queryset(users, request)serializer = UserSerializer(result_page, many=True)return paginator.get_paginated_response(serializer.data)

限制偏移分页 (LimitOffsetPagination)

# 客户端请求示例: /api/users/?limit=10&offset=20
from rest_framework.pagination import LimitOffsetPaginationclass 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)

游标分页 (CursorPagination)

适用于大型数据集和实时数据流,基于"游标"而非页码:

# 客户端请求示例: /api/users/?cursor=cD0yMDIwLTAxLTAxKzAwJTNBMDAlM0EwMA==
from rest_framework.pagination import CursorPaginationclass MyCursorPagination(CursorPagination):ordering = '-created_at'  # 排序字段page_size = 10class 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 中非常强大的基础视图类,它提供了与数据库模型和序列化器交互的通用功能,包括:

  1. 查询集管理(queryset 和 get_queryset())
  2. 序列化器管理(serializer_class 和 get_serializer())
  3. 对象查找(lookup_field 和 get_object())
  4. 分页(pagination_class 和 paginate_queryset())
  5. 过滤(filter_backends 和 filter_queryset())

通过合理使用这些功能,可以大大简化 API 开发工作,提高代码的可维护性和可读性。同时,GenericAPIView 也是 DRF 中更高级视图(如 ListAPIView、RetrieveAPIView 等)的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/891405.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

k8s部署dashboard kubepi

1. KubePi 简介KubePi 是一个开源的 Kubernetes 多集群管理面板,提供以下核心功能: 多集群管理:支持接入多个 Kubernetes 集群统一管理。 可视化操作:提供资源(Pod、Deployment、Service 等)的创建、删除、监控功能。 权限控制:基于角色的访问控制(RBAC),支持多租户隔…

强大数定律与弱大数定律的区别

先来讲一下弱大数定律吧,这个比较好理解 弱大数定律的标准形式是这样的: \[\forall \epsilon>0,\underset{n\rightarrow\infty}{\lim}P(|\bar{X}_n-\mu|<\epsilon)=1 \]这里注意的是我们的极限符号是包含概率的,我们按照数列极限的定义将其写开: \[\forall \epsilon&…

分布式锁—1.原理算法和使用建议

大纲 1.Redis分布式锁的8大问题 2.Redis的RedLock算法分析 3.基于Redis和zk的分布式锁实现原理 4.Redis分布式锁的问题以及使用建议1.Redis分布式锁的8大问题 (1)非原子操作(set+lua) (2)忘了释放锁(手动+超时) (3)释放了其他线程的锁(lua+唯一值) (4)加锁失败的处理(自旋+睡眠…

理解Rust引用及其生命周期标识(上)

写在前面 作为Rust开发者,你是否还没有完全理解引用及其生命周期?是否处于教程一看就会,但在实际开发过程中不知所措?本文将由浅入深,手把手教你彻底理解Rust引用与生命周期。 关于本文的理解门槛 本文主要面向的是已经基本上了解过Rust这门语言,对引用以及生命周期(及其…

TCP/IP协议栈相关知识

为什么提出TCP/IP参考模型OSI参考模型比较复杂TCP和IP两大协议在网络上广泛使用 三种参考模型如今用的最多的是TCP/IP五层模型,而OSI七层模型更多用于学习。 TCP/IP常见协议要了解协议对应的传输层端口号,因为计算机是通过端口号分辨所使用的是那种协议。 应用层要了解常见协…

Vulkan环境配置 | vscode+msvc 解决方案

Vulkan环境配置 | vscode+msvc 解决方案 前言 如果作为Windows 11侠的你是一个vscode爱好者,凑巧电脑上还安装有Visual Studio,这个时候你突然想配置一个Vulkan开发环境。作为minimalist的你可不希望在电脑上安装任何额外的组件,那么这篇安装指南一定适合你。 准备工作 你需…

mysql索引原理简单说明

本次使用的是mysql5.7.17 首先说下索引中的组合索引,即多个字段组合的索引就叫做组合索引,但是什么时候会生效,什么时候会失效,我不是很清楚 首先建个表造点数据看下情况,CREATE TABLE `bank` (`id` INT(11) PRIMARY KEY AUTO_INCREMENT COMMENT 主键,`bank_end` VARCHAR…

Vulnhub-Troll-1靶机-ftp匿名登录+流量包分析+hydra爆破+ssh登录脚本提权

一、靶机搭建 选择扫描虚拟机选择路径即可二、信息收集 扫ip 靶机ip:192.168.108.144扫开放端口 开放了ftp服务扫版本服务信息信息如下 21/tcp open ftp vsftpd 3.0.2 22/tcp open ssh OpenSSH 6.6.1p1 Ubuntu 2ubuntu2 (Ubuntu Linux; protocol 2.0) 80/tcp open …

八、(了解即可)MyBatis懒加载(或者叫延迟加载)

八、(了解即可)MyBatis懒加载(或者叫延迟加载)@目录八、懒加载(了解即可)8.1 为啥需要懒加载?8.2 懒加载是什么?8.3 开启方式8.4 既然fetchType可以控制懒加载那么我仅仅配置fetchType不配置全局的可以吗?8.5 aggressiveLazyLoading是做什么么的?8.6 注意点8.7 案例验证懒加…

基于惯性加权PSO优化的目标函数最小值求解matlab仿真

1.程序功能描述基于惯性加权PSO优化的目标函数最小值求解matlab仿真。 2.测试软件版本以及运行结果展示MATLAB2022A版本运行 (完整程序运行后无水印) 3.核心程序yfits = []; % 主循环开始 for iter =1: Miteryfit = zeros(Npop,1); % 初始化函数值数组% 更新粒子速度…

题解:at_abc391_e Hierarchical Majority Vote

对于一个长度为 \(3^n\) 的 01 字符串 \(B = B_1B_2\dots B_{3^n}\),定义一种操作获得长度为 \(3^{n-1}\) 的 01 字符串 \(C = C_1C_2\dots C_{3^{n-1}}\):对于 \(i = 1,2,\dots,3^{n-1}\),令 \(C_i\) 为 \(B_{3i}\)、\(B_{3i-1}\)、\(B_{3i-2}\) 中出现次数最多的字符。现给…