随笔 - 77  文章 - 0  评论 - 0  阅读 - 21363 

GenericAPIView里面的get_serializer_class

"""
GenericAPIView的get_serializer_class主要的作用就是允许我们通过代码调整,进行判断加载不同的序列化器类到视图类中
get_serializer_class 本身就是属于提供给GenericAPIView获取序列化器类名的.
"""
from rest_framework.viewsets import ModelViewSet
from .serializers import StudentModelSerializer,StudentListModelSerializer
from rest_framework.generics import GenericAPIView
class Student2ModelViewSet(ModelViewSet):
  queryset = Student.objects.all()
  def get_serializer_class(self):
      print(self.action)
      """可以通过判断逻辑加载不同的序列化器类"""
      if self.action == "list":
            return StudentListModelSerializer
      else:
          return StudentModelSerializer

序列化器,代码:

from rest_framework import serializers
from students.models import Student
class StudentModelSerializer(serializers.ModelSerializer):
   """详情页学生序列化器"""
   class Meta:
       model = Student
       fields = "__all__"

class StudentListModelSerializer(serializers.ModelSerializer):
   """列表页学生序列化器"""
   class Meta:
       model = Student
       fields = ["id","name","class_number"]

路由代码:

    path("sera/students/", views.Student2ModelViewSet.as_view({
       "get": "list",
       "post": "create"
  })),
   re_path("^sera/students/(?P<pk>\d+)/$", views.Student2ModelViewSet.as_view({
       "get": "retrieve",
       "put": "update",
       "delete": "destroy",
  })),

 

 

2. 路由Routers

对于视图集ViewSet,我们除了可以自己手动在路由url.py中设置当前客户端的jhttp请求方式与动作action之间的对应关系外,在drf中还可以使用Routers路由类来自动帮我们快速生成视图集的路由列表。

REST framework提供了两个router路由类,这两个路由类几乎没区别。后面我们讲下他们的小区分。

  • SimpleRouter

  • DefaultRouter

2.1 使用方法

1) 创建router对象,并注册视图集,例如

from rest_framework import routers

router = routers.DefaultRouter()
router.register('router/student', StudentModelViewSet, basename='student')

register(prefix, viewset, base_name)

  • prefix 该视图集的路由前缀

  • viewset 视图集

  • basename 路由别名的前缀

如上述代码会形成的路由如下:

^books/$    name: book-list
^books/{pk}/$   name: book-detail

2)添加路由数据

可以有两种方式:

urlpatterns = [
  ...
]
urlpatterns += router.urls

urlpatterns = [
  ...
   url(r'^', include(router.urls))
]

使用路由类给视图集生成了路由地址

from rest_framework.viewsets import ModelViewSet,ReadOnlyModelViewSet
class StudentModelViewSet(ModelViewSet):
   queryset = Student.objects.all()
   serializer_class = StudentModelSerializer

   def login(self,request):
       """学生登录功能"""
       print(self.action)
       return Response({"message":"登录成功"})

路由代码:

from django.urls import path, re_path
from . import views
urlpatterns = [
  ...
]

"""使用drf提供路由类router给视图集生成路由列表"""
# 实例化路由类
# drf提供一共提供了两个路由类给我们使用,他们用法一致,功能几乎一样
from rest_framework.routers import DefaultRouter
router = DefaultRouter()

# 注册视图集
# router.register("路由前缀",视图集类)
router.register("router_stu",views.StudentModelViewSet)

# 把生成的路由列表追加到urlpatterns
print( router.urls )
urlpatterns += router.urls

上面的代码就成功生成了路由地址[增/删/改/查一条/查多条的功能],但是不会自动我们在视图集自定义方法的路由。

所以我们如果也要给自定义方法生成路由,则需要进行action动作的声明。

 

2.2 视图集中新增定义类方法可以通过action装饰器来声明

如果想要在视图集中自定义类方法,我们还是可以路由类生成路由信息的,不过我们需要使用rest_framework.decorators.action装饰器对自定义的类方法进行装饰。

以action装饰器装饰的方法名会作为action动作名,与list、retrieve等同。

action装饰器可以接收两个参数:

  • methods: 声明该action对应的请求方式,列表传递

  • detail

    : 声明该action的路径是否与单一资源对应,及是否是

    xxx/<pk>/action方法名/
    • True 表示路径格式是xxx/<pk>/action方法名/

    • False 表示路径格式是xxx/action方法名/

举例:

from rest_framework.viewsets import ModelViewSet
from .serializers import StudentModelSerializer,StudentListModelSerializer
from rest_framework.generics import GenericAPIView
from rest_framework.decorators import action
class Student2ModelViewSet(ModelViewSet):
   queryset = Student.objects.all()
   def get_serializer_class(self):
       print(self.action)
       """可以通过判断逻辑加载不同的序列化器类"""
       if self.action == "list":
           return StudentListModelSerializer
       else:
           return StudentModelSerializer

   """
  给视图集添加自定义方法,使用action装饰器可以让路由类自动生成对应的路由地址
  @action(methods,detail,url_path,url_name)

  参数1 methods, 列表, 表示允许客户端通过那些http请求访问当前视图方法,例如: ["get"]
  参数2 detail   布尔值, 表示是否需要在生成路由的地址,给url地址生成pk值参数
  参数3 url_path 字符串,表示生成路由时,指向当前视图方法的路由地址
  参数4 utl_name 字符串,表示生成路由时,指向当前视图方法的路由别名
  例如,获取学生年龄最小的2个人
  """
   @action(methods=["get","post"], detail=True,url_path="min",url_name="min5")
   def get_min_5(self,request):

       return Response("2个最小的学生信息")

由路由器自动为此视图集自定义action方法形成的路由会是如下内容:

demo/ ^router/students/$ [name='sera-student-list']
demo/ ^router/students/(?P<pk>[^/.]+)/$ [name='sera-student-detail']
demo/ ^router/students/(?P<pk>[^/.]+)/min/$ [name='sera-student-min5']

 

2.3 路由router形成URL的方式

1) SimpleRouter

2)DefaultRouter

DefaultRouter与SimpleRouter的区别是,DefaultRouter会多附带一个默认的API根视图,返回一个包含所有列表视图的超链接响应数据。

 

 

为了方便接下来的学习,我们创建一个新的子应用 opt

python manage.py startapp opt

 

因为接下来的功能中需要使用到登陆功能,所以我们使用django内置admin站点并创建一个管理员.

python manage.py createsuperuser 

创建管理员以后,访问admin站点,先修改站点的语言配置

settings.py

LANGUAGE_CODE = 'zh-hans'

TIME_ZONE = 'Asia/Shanghai'

 

访问admin 站点效果:

 

 

1. 认证Authentication

可以在配置文件中配置全局默认的认证方案

/home/moluo/.virtualenvs/drfdemo/lib/python3.6/site-packages/rest_framework/settings.py

REST_FRAMEWORK = {
   'DEFAULT_AUTHENTICATION_CLASSES': (
       'rest_framework.authentication.SessionAuthentication',  # session认证
       'rest_framework.authentication.BasicAuthentication',   # 基本认证
  )
}

也可以在每个视图中通过设置authentication_classess属性来设置

from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.views import APIView

class ExampleView(APIView):
   # 类属性
   authentication_classes = [SessionAuthentication, BasicAuthentication]
   def get(self,request):
       pass
   

认证失败会有两种可能的返回值,这个需要我们配合权限组件来使用:

  • 401 Unauthorized 未认证

  • 403 Permission Denied 权限被禁止

 

 

 

2. 权限Permissions

权限控制可以限制用户对于视图的访问和对于具体数据对象的访问。

  • 在执行视图的as_view()方法的dispatch()方法前,会先进行视图访问权限的判断

  • 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断

使用

可以在配置文件中全局设置默认的权限管理类,如

REST_FRAMEWORK = {
  ....
   
   'DEFAULT_PERMISSION_CLASSES': (
       'rest_framework.permissions.IsAuthenticated',
  )
}

如果未指明,则采用如下默认配置

'DEFAULT_PERMISSION_CLASSES': (
  'rest_framework.permissions.AllowAny',
)

也可以在具体的视图中通过permission_classes属性来设置,如

from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

class ExampleView(APIView):
   permission_classes = (IsAuthenticated,)
  ...

提供的权限

  • AllowAny 允许所有用户

  • IsAuthenticated 仅通过登录认证的用户

  • IsAdminUser 仅管理员用户

  • IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。

举例

from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView

class StudentAPIView(RetrieveAPIView):
   queryset = Student.objects.all()
   serializer_class = StudentSerializer
   authentication_classes = [SessionAuthentication]
   permission_classes = [IsAuthenticated]

 

自定义权限

如需自定义权限,需继承rest_framework.permissions.BasePermission父类,并实现以下两个任何一个方法或全部

  • .has_permission(self, request, view)

    是否可以访问视图, view表示当前视图对象

  • .has_object_permission(self, request, view, obj)

    是否可以访问数据对象, view表示当前视图, obj为模型数据对象

例如:

在当前子应用下,创建一个权限文件permissions.py中声明自定义权限类:

from rest_framework.permissions import BasePermission

class IsXiaoMingPermission(BasePermission):
   def has_permission(self, request, view):
       if( request.user.username == "xiaoming" ):
           return True

 

from .permissions import IsXiaoMingPermission
class StudentViewSet(ModelViewSet):
   queryset = Student.objects.all()
   serializer_class = StudentSerializer
   permission_classes = [IsXiaoMingPermission]

 

 

3. 限流Throttling

可以对接口访问的频次进行限制,以减轻服务器压力,或者实现特定的业务。

一般用于付费购买次数,投票等场景使用.

使用

可以在配置文件中,使用DEFAULT_THROTTLE_CLASSESDEFAULT_THROTTLE_RATES进行全局配置,

REST_FRAMEWORK = {
   'DEFAULT_THROTTLE_CLASSES': (
       'rest_framework.throttling.AnonRateThrottle',
       'rest_framework.throttling.UserRateThrottle'
  ),
   'DEFAULT_THROTTLE_RATES': {
       'anon': '100/day',
       'user': '1000/day'
  }
}

DEFAULT_THROTTLE_RATES 可以使用 second, minute, hourday来指明周期。

也可以在具体视图中通过throttle_classess属性来配置,如

from rest_framework.throttling import UserRateThrottle
from rest_framework.views import APIView

class ExampleView(APIView):
   throttle_classes = (UserRateThrottle,)
  ...

可选限流类

1) AnonRateThrottle

限制所有匿名未认证用户,使用IP区分用户。

使用DEFAULT_THROTTLE_RATES['anon'] 来设置频次

2)UserRateThrottle

限制认证用户,使用User id 来区分。

使用DEFAULT_THROTTLE_RATES['user'] 来设置频次

3)ScopedRateThrottle

限制用户对于每个视图的访问频次,使用ip或user id。

例如:

class ContactListView(APIView):
  throttle_scope = 'contacts'
  ...

class ContactDetailView(APIView):
  throttle_scope = 'contacts'
  ...

class UploadView(APIView):
  throttle_scope = 'uploads'
  ...
REST_FRAMEWORK = {
  'DEFAULT_THROTTLE_CLASSES': (
      'rest_framework.throttling.ScopedRateThrottle',
  ),
  'DEFAULT_THROTTLE_RATES': {
      'contacts': '1000/day',
      'uploads': '20/day'
  }
}

实例

全局配置中设置访问频率

    'DEFAULT_THROTTLE_RATES': {
       'anon': '3/minute',
       'user': '10/minute'
  }

 

from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.generics import RetrieveAPIView
from rest_framework.throttling import UserRateThrottle

class StudentAPIView(RetrieveAPIView):
   queryset = Student.objects.all()
   serializer_class = StudentSerializer
   authentication_classes = [SessionAuthentication]
   permission_classes = [IsAuthenticated]
   throttle_classes = (UserRateThrottle,)

 

 

4. 过滤Filtering

对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。

pip install django-filter

在配置文件中增加过滤后端的设置:

INSTALLED_APPS = [
  ...
   'django_filters',  # 需要注册应用,
]

REST_FRAMEWORK = {
  ...
   'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',)
}

在视图类中添加类属性filter_fields,指定可以过滤的字段

class StudentListView(ListAPIView):
   queryset = Student.objects.all()
   serializer_class = StudentSerializer
   filter_fields = ('age', 'sex')

# 127.0.0.1:8000/four/students/?sex=1

 

 

5. 排序

对于列表数据,REST framework提供了OrderingFilter过滤器来帮助我们快速指明数据按照指定字段进行排序。

使用方法:

在类视图中设置filter_backends,使用rest_framework.filters.OrderingFilter过滤器,REST framework会在请求的查询字符串参数中检查是否包含了ordering参数,如果包含了ordering参数,则按照ordering参数指明的排序字段对数据集进行排序。

前端可以传递的ordering参数的可选字段值需要在ordering_fields中指明。

示例:

class StudentListView(ListAPIView):
   queryset = Student.objects.all()
   serializer_class = StudentModelSerializer
   filter_backends = [OrderingFilter]
   ordering_fields = ('id', 'age')

# 127.0.0.1:8000/books/?ordering=-age
# -id 表示针对id字段进行倒序排序
# id 表示针对id字段进行升序排序

 

如果需要在过滤以后再次进行排序,则需要两者结合!

全局配置下的过滤组件不能和排序组件一起使用,只支持局部配置的过滤组件和排序组件一起使用。

from rest_framework.generics import ListAPIView
from students.models import Student
from .serializers import StudentModelSerializer
from django_filters.rest_framework import DjangoFilterBackend
class Student3ListView(ListAPIView):
   queryset = Student.objects.all()
   serializer_class = StudentModelSerializer
   filter_fields = ('age', 'sex')
   # 因为局部配置会覆盖全局配置,所以需要重新把过滤组件核心类再次声明,
   # 否则过滤功能会失效
   filter_backends = [OrderingFilter,DjangoFilterBackend]
   ordering_fields = ('id', 'age')
 

 

 

6. 分页Pagination

REST framework提供了分页的支持。

我们可以在配置文件中设置全局的分页方式,如:

REST_FRAMEWORK = {
   'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
   'PAGE_SIZE': 100  # 每页数目
}
如果在配置settings.py文件中, 设置了全局分页,那么在drf中凡是调用了ListModelMixin的list(),都会自动分页。如果项目中出现大量需要分页的数据,只有少数部分的分页,则可以在少部分的视图类中关闭分页功能。
class 视图类(ListAPIView):
pagination_class = None
   

 

也可通过自定义Pagination类,来为视图添加不同分页行为。在视图中通过pagination_clas属性来指明。

class LargeResultsSetPagination(PageNumberPagination):
   page_size = 1000
   page_size_query_param = 'page_size'
   max_page_size = 10000
class BookDetailView(RetrieveAPIView):
   queryset = BookInfo.objects.all()
   serializer_class = BookInfoSerializer
   pagination_class = LargeResultsSetPagination

 

可选分页器

1) PageNumberPagination

前端访问网址形式:

GET  http://127.0.0.1:8000/students/?page=4

可以在子类中定义的属性:

  • page_size 每页数目

  • page_query_param 前端发送的页数关键字名,默认为"page"

  • page_size_query_param 前端发送的每页数目关键字名,默认为None

  • max_page_size 前端最多能设置的每页数量

# 声明分页的配置类
from rest_framework.pagination import PageNumberPagination
class StandardPageNumberPagination(PageNumberPagination):
   # 默认每一页显示的数据量
   page_size = 2
   # 允许客户端通过get参数来控制每一页的数据量
   page_size_query_param = "size"
   max_page_size = 10
   # 自定义页码的参数名
   page_query_param = "p"

class StudentAPIView(ListAPIView):
   queryset = Student.objects.all()
   serializer_class = StudentModelSerializer
   pagination_class = StandardPageNumberPagination

# 127.0.0.1/four/students/?p=1&size=5

2)LimitOffsetPagination

前端访问网址形式:

GET http://127.0.0.1/four/students/?limit=100&offset=100

可以在子类中定义的属性:

  • default_limit 默认限制,默认值与PAGE_SIZE设置一直

  • limit_query_param limit参数名,默认'limit'

  • offset_query_param offset参数名,默认'offset'

  • max_limit 最大limit限制,默认None

from rest_framework.pagination import LimitOffsetPagination
class StandardLimitOffsetPagination(LimitOffsetPagination):
   # 默认每一页查询的数据量,类似上面的page_size
   default_limit = 2
   limit_query_param = "size"
   offset_query_param = "start"

class StudentAPIView(ListAPIView):
   queryset = Student.objects.all()
   serializer_class = StudentModelSerializer
   # 调用页码分页类
   # pagination_class = StandardPageNumberPagination
   # 调用查询偏移分页类
   pagination_class = StandardLimitOffsetPagination

 

 

7. 异常处理 Exceptions

REST framework提供了异常处理,我们可以自定义异常处理函数。例如我们想在要创建一个自定义异常函数,

这个函数,我们保存到当前子应用opt中[注意,开发时,我们会找个独立的公共目录来保存这种公共的函数/工具/类库]。

from rest_framework.views import exception_handler

def custom_exception_handler(exc, context):
   # 先调用REST framework默认的异常处理方法获得标准错误响应对象
   response = exception_handler(exc, context)

   # 在此处补充自定义的异常处理
   if response is None:
       response.data['status_code'] = response.status_code

   return response

在配置文件中声明自定义的异常处理

REST_FRAMEWORK = {
   'EXCEPTION_HANDLER': 'my_project.my_app.utils.custom_exception_handler'
}

如果未声明,会采用默认的方式,如下

rest_frame/settings.py

REST_FRAMEWORK = {
   'EXCEPTION_HANDLER': 'rest_framework.views.exception_handler'
}

例如:

补充上处理关于数据库的异常

from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework import status
from django.db import DatabaseError

def exception_handler(exc, context):
   response = drf_exception_handler(exc, context)

   if response is None:
       view = context['view']
       if isinstance(exc, DatabaseError):
           print('[%s]: %s' % (view, exc))
           response = Response({'detail': '服务器内部错误'}, status=status.HTTP_507_INSUFFICIENT_STORAGE)

   return response

 

REST framework定义的异常

  • APIException 所有异常的父类

  • ParseError 解析错误

  • AuthenticationFailed 认证失败

  • NotAuthenticated 尚未认证

  • PermissionDenied 权限决绝

  • NotFound 未找到

  • MethodNotAllowed 请求方式不支持

  • NotAcceptable 要获取的数据格式不支持

  • Throttled 超过限流次数

  • ValidationError 校验失败

也就是说,很多的没有在上面列出来的异常,就需要我们在自定义异常中自己处理了。

 

 

 

8. 自动生成接口文档

REST framework可以自动帮助我们生成接口文档。

接口文档以网页的方式呈现。

自动接口文档能生成的是继承自APIView及其子类的视图。

8.1. 安装依赖

REST framewrok生成接口文档需要coreapi库的支持。

pip install coreapi

8.2. 设置接口文档访问路径

在总路由中添加接口文档路径。

文档路由对应的视图配置为rest_framework.documentation.include_docs_urls

参数title为接口文档网站的标题。

from rest_framework.documentation import include_docs_urls

urlpatterns = [
  ...
   path('docs/', include_docs_urls(title='站点页面标题'))
]

在settings.py中配置接口文档。

REST_FRAMEWORK = {
   # 。。。 其他选项
   # 接口文档
   'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.AutoSchema',
}

 

8.3. 文档描述说明的定义位置

1) 单一方法的视图,可直接使用类视图的文档字符串,如

class BookListView(generics.ListAPIView):
   """
  返回所有图书信息.
  """

2)包含多个方法的视图,在类视图的文档字符串中,分开方法定义,如

class BookListCreateView(generics.ListCreateAPIView):
   """
  get:
  返回所有图书信息.

  post:
  新建图书.
  """

3)对于视图集ViewSet,仍在类视图的文档字符串中封开定义,但是应使用action名称区分,如

class BookInfoViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, GenericViewSet):
   """
  list:
  返回图书列表数据

  retrieve:
  返回图书详情数据

  latest:
  返回最新的图书数据

  read:
  修改图书的阅读量
  """

8.4. 访问接口文档网页

浏览器访问 127.0.0.1:8000/docs/,即可看到自动生成的接口文档。

两点说明:

1) 视图集ViewSet中的retrieve名称,在接口文档网站中叫做read

2)参数的Description需要在模型类或序列化器类的字段中以help_text选项定义,如:

class Student(models.Model):
  ...
   age = models.IntegerField(default=0, verbose_name='年龄', help_text='年龄')
  ...

class StudentSerializer(serializers.ModelSerializer):
   class Meta:
       model = Student
       fields = "__all__"
       extra_kwargs = {
           'age': {
               'required': True,
               'help_text': '年龄'
          }
      }

 

posted on   rider_yang  阅读(213)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示