Django DRF路由与扩展功能的实现


Posted in Python onJune 03, 2020

一. 视图集与路由的使用

使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

  • list() 提供一组数据
  • retrieve() 提供单个数据
  • create() 创建数据
  • update() 保存数据
  • destory() 删除数据

ViewSet视图集类不再实现get()、post()等方法,而是实现动作 action 如 list() 、create() 等。

视图集只在使用as_view()方法的时候,才会将action动作与具体请求方式对应上。

1. 常用的视图集父类

1.ViewSet

继承自APIView 与 ViewSetMixin作用也与APIView基本类似,提供了身份认证、权限校验、流量管理等。

ViewSet主要通过继承ViewSetMixin来实现在调用as_view()时传入字典(如{'get':'list'})的映射处理工作。

在ViewSet中,没有提供任何动作action方法,需要我们自己实现action方法。

2.GenericViewSet

使用ViewSet通常并不方便,因为list、retrieve、create、update、destory等方法都需要自己编写,而这些方法与前面讲过的Mixin扩展类提供的方法同名,所以我们可以通过继承Mixin扩展类来复用这些方法而无需自己编写。但是Mixin扩展类依赖与GenericAPIView,所以还需要继承GenericAPIView。

GenericViewSet就帮助我们完成了这样的继承工作,继承自GenericAPIView与ViewSetMixin,在实现了调用as_view()时传入字典(如{'get':'list'})的映射处理工作的同时,还提供了GenericAPIView提供的基础方法,可以直接搭配Mixin扩展类使用。

3.ModelViewSet

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestoryModelMixin。

4.ReadOnlyModelViewSet

继承自GenericViewSet,同时包括了ListModelMixin、RetrieveModelMixin。

下面我们还是通过案例,为大家演示吧!

首先,先创建一个子应用。

python3 manage.py startapp collect

5. 在collect下新建序列化器类

# collect下的serializers.py文件

from students.models import Student
from rest_framework import serializers


class StudentModelSerializer(serializers.ModelSerializer):

  class Meta:
    model = Student
    fields = ["id", "name", "age", "sex"]
    extra_kwargs = {
      "name": {"max_length": 10, "min_length": 4},
      "age": {"max_value": 150, "min_value": 0},
    }

  def validate_name(self, data):
    if data == "root":
      raise serializers.ValidationError("用户名不能为root!")
    return data

  def validate(self, attrs):
    name = attrs.get('name')
    age = attrs.get('age')

    if name == "alex" and age == 22:
      raise serializers.ValidationError("alex在22时的故事。。。")

    return attrs


class StudentInfoModelSerializer(serializers.ModelSerializer):
  class Meta:
    model = Student
    fields = ["id", "name"]

6. collect下的urls.py

from django.urls import path, re_path
from collect import views

urlpatterns = [
  # 不要在同一个路由的as_view中书写两个同样的键的http请求,会产生覆盖!!!
  # ViewSet
  path('student1/', views.Student1ViewSet.as_view({"get": "get_5"})),
  path('student1/get_5_girl/', views.Student1ViewSet.as_view({"get": "get_5_girl"})),
  re_path(r'^student1/(?P<pk>\d+)/$', views.Student1ViewSet.as_view({"get": "get_one"})),
  # GenericViewSet
  path('student2/', views.Student3GenericViewSet.as_view({"get": "get_5"})),
  path('student2/get_5_girl/', views.Student3GenericViewSet.as_view({"get": "get_5_girl"})),
  # GenericViewSet,可以和模型类进行组合快速生成基本的API接口
  path("students3/", views.Student4GenericViewSet.as_view({"get": "list", "post": "create"})),
  # ModelViewSet 默认提供了5个API接口
  path("students4/", views.Student5ModelViewSet.as_view({"post": "create", "get": "list"})),
  re_path(r"^students4/(?P<pk>\d+)/$", views.Student5ModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
  # ReadOnlyModelViewSet
  path("students5/", views.Student6ReadOnlyModelViewSet.as_view({"get": "list"})),
  re_path(r"^students5/(?P<pk>\d+)/$", views.Student6ReadOnlyModelViewSet.as_view({"get": "retrieve"})),

  # 一个视图类中调用多个序列化器
  path("student8/", views.Student8GenericAPIView.as_view()),

  # 一个视图集中调用多个序列化器
  path("student9/", views.Student9ModelViewSet.as_view({"get": "list"})),
  re_path(r"^student9/(?P<pk>\d+)/$", views.Student9ModelViewSet.as_view({"get": "retrieve"})),
]


"""
有了视图集以后,视图文件中多个视图类可以合并成一个,但是,路由的代码就变得复杂了,
需要我们经常在as_view方法 ,编写http请求和视图方法的对应关系,
事实上,在路由中,DRF也提供了一个路由类给我们对路由的代码进行简写。
当然,这个路由类仅针对于 视图集 才可以使用。
"""

# 路由类默认只会给视图集中的基本5个API生成地址[ 获取一条,获取多条,添加.删除,修改数据 ]
from rest_framework.routers import DefaultRouter
# 实例化路由类
router = DefaultRouter()
# router.register("访问地址前缀","视图集类","访问别名")
# 注册视图视图集类
router.register("student7", views.Student7ModelViewSet)

# 把路由列表注册到django项目中
urlpatterns += router.urls

7. collect下的views.py

"""ViewSet视图集,继承于APIView,所以APIView有的功能,它都有,APIView没有的功能,它也没有"""
from rest_framework.viewsets import ViewSet
from students.models import Student
from .serializers import StudentModelSerializer
from rest_framework.response import Response


class Student1ViewSet(ViewSet):
  def get_5(self, request):
    student_list = Student.objects.all()[:5]

    serializer = StudentModelSerializer(instance=student_list, many=True)

    return Response(serializer.data)

  def get_one(self, request, pk):
    student = Student.objects.get(pk=pk)

    serializer = StudentModelSerializer(instance=student)

    return Response(serializer.data)

  def get_5_girl(self, request):
    student_list = Student.objects.filter(sex=False)[:5]

    serializer = StudentModelSerializer(instance=student_list, many=True)

    return Response(serializer.data)


"""如果希望在视图集中调用GenericAPIView的功能,则可以采用下面方式"""
from rest_framework.generics import GenericAPIView


class Student2ViewSet(ViewSet, GenericAPIView):
  queryset = Student.objects.all()
  serializer_class = StudentModelSerializer

  def get_5(self, request):
    student_list = self.get_queryset()[:5]

    serializer = StudentModelSerializer(instance=student_list, many=True)

    return Response(serializer.data)

  def get_one(self, request, pk):
    student = self.get_object()

    serializer = StudentModelSerializer(instance=student)

    return Response(serializer.data)

  def get_5_girl(self, request):
    student_list = self.get_queryset().filter(sex=False)[:5]

    serializer = StudentModelSerializer(instance=student_list, many=True)

    return Response(serializer.data)

"""
上面的方式,虽然实现视图集中调用GenericAPIView,但是我们要多了一些类的继承。
所以我们可以直接继承 GenericViewSet
"""
from rest_framework.viewsets import GenericViewSet


class Student3GenericViewSet(GenericViewSet):
  serializer_class = StudentModelSerializer
  queryset = Student.objects.all()

  def get_5(self, request):
    student_list = self.get_queryset()[:5]

    serializer = self.get_serializer(instance=student_list, many=True)

    return Response(serializer.data)

  def get_5_girl(self, request):
    student_list = self.get_queryset().filter(sex=False)[:5]

    serializer = self.get_serializer(instance=student_list, many=True)

    return Response(serializer.data)

"""
在使用GenericViewSet时,虽然已经提供了基本调用数据集(queryset)和序列化器属性,但是我们要编写一些基本的
API时,还是需要调用DRF提供的模型扩展类 [Mixins]
"""
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import ListModelMixin, CreateModelMixin


class Student4GenericViewSet(GenericViewSet, ListModelMixin, CreateModelMixin):
  queryset = Student.objects.all()
  serializer_class = StudentModelSerializer


from rest_framework.viewsets import ModelViewSet


class Student5ModelViewSet(ModelViewSet):
  queryset = Student.objects.all()
  serializer_class = StudentModelSerializer


# 只读模型视图集
from rest_framework.viewsets import ReadOnlyModelViewSet


class Student6ReadOnlyModelViewSet(ReadOnlyModelViewSet):
  queryset = Student.objects.all()
  serializer_class = StudentModelSerializer


# 路由的使用
from rest_framework.decorators import action


class Student7ModelViewSet(ModelViewSet):
  queryset = Student.objects.all()
  serializer_class = StudentModelSerializer

  # methods 指定允许哪些http请求访问当前视图方法
  # detail 指定生成的路由地址中是否要夹带pk值,True为需要
  @action(methods=["GET"], detail=False)
  def get_6(self, request):
    serilizer = self.get_serializer(instance=self.get_queryset().get(pk=6))
    return Response(serilizer.data)


"""在多个视图类合并成一个视图类以后,那么有时候会出现一个类中需要调用多个序列化器"""

"""1. 在视图类中调用多个序列化器"""
"""原来的视图类中基本上一个视图类只会调用一个序列化器,当然也有可能要调用多个序列化器"""
from .serializers import StudentInfoModelSerializer


class Student8GenericAPIView(GenericAPIView):
  queryset = Student.objects.all()

  # GenericAPI内部调用序列化器的方法,我们可以重写这个方法来实现根据不同的需求来调用不同的序列化器
  def get_serializer_class(self):
    if self.request.method == "GET":
      # 2个字段
      return StudentInfoModelSerializer
    return StudentModelSerializer

  def get(self, request):
    """获取所有数据的id和name"""
    student_list = self.get_queryset()

    serializer = self.get_serializer(instance=student_list, many=True)

    return Response(serializer.data)

  def post(self, request):
    """添加数据"""
    data = request.data
    serializer = self.get_serializer(data=data)
    serializer.is_valid(raise_exception=True)
    serializer.save()
    return Response(serializer.data)


"""2. 在一个视图集中调用多个序列化器"""

class Student9ModelViewSet(ModelViewSet):
  queryset = Student.objects.all()

  """要求:
      列表数据list,返回2个字段,
      详情数据retrieve,返回所有字段,
  """
  def get_serializer_class(self):
    # 本次客户端请求的视图方法名 self.action
    if self.action == "list":
      return StudentInfoModelSerializer
    return StudentModelSerializer

二. 扩展功能

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

python3 manage.py startapp opt

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

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

LANGUAGE_CODE = 'zh-hans'

1. 认证Authentication

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

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

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

opt下的urls.py

from django.urls import path
from opt import views

urlpatterns = [
  path('auth1/', views.Demo1APIView.as_view()),
  path('auth2/', views.Demo2APIView.as_view()),
]

opt下的views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAdminUser

"""用户的认证和权限识别"""


class Demo1APIView(APIView):
  """只允许登录后的用户访问"""
  permission_classes = [IsAuthenticated]

  def get(self, request):
    """个人中心"""
    return Response("个人中心")


class Demo2APIView(APIView):
  """只允许管理员访问"""
  permission_classes = [IsAdminUser]

  def get(self, request):
    """个人中心2"""
    return Response("个人中心2")

2. 权限Permissions

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

  • 在执行视图的dispatch()方法前,会先进行视图访问权限的判断
  • 在通过get_object()获取具体对象时,会进行模型对象访问权限的判断

内置提供的权限:

  • AllowAny 允许所有用户
  • IsAuthenticated 仅通过认证的用户
  • IsAdminUser 仅管理员用户
  • IsAuthenticatedOrReadOnly 已经登陆认证的用户可以对数据进行增删改操作,没有登陆认证的只能查看数据。

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

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

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

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

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

opt下的urls.py

urlpatterns = [
  path('auth1/', views.Demo1APIView.as_view()),
  path('auth2/', views.Demo2APIView.as_view()),
  # 自定义权限
  path('auth3/', views.Demo3APIView.as_view()),
]

opt下的views.py

# 自定义权限
from rest_framework.permissions import BasePermission


class MyPermission(BasePermission):
  def has_permission(self, request, view):
    """
    针对访问视图进行权限判断
    :param request: 本次操作的http请求对象
    :param view: 本次访问路由对应的视图对象
    :return:
    """
    if request.user.username == "xiaoming":
      return True
    return False


class Demo3APIView(APIView):
  permission_classes = [MyPermission]

  def get(self, request):
    """个人中心3"""
    return Response("个人中心3")

3. 限流Throttling

可以对接口访问的频次进行限制,以减轻服务器压力。

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

可以在配置文件中,使用DEFAULT_THROTTLE_CLASSES 和 DEFAULT_THROTTLE_RATES进行全局配置

REST_FRAMEWORK = {
  # 限流
  'DEFAULT_THROTTLE_CLASSES': ( # 对全局进行设置
    'rest_framework.throttling.AnonRateThrottle',
    'rest_framework.throttling.UserRateThrottle'
  ),
  'DEFAULT_THROTTLE_RATES': {
    'anon': '3/hour',
    'user': '3/minute',
  }
}

DEFAULT_THROTTLE_RATES 可以使用 second, minute, hour 或day来指明周期。

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

opt下的urls.py

urlpatterns = [
  path('auth1/', views.Demo1APIView.as_view()),
  path('auth2/', views.Demo2APIView.as_view()),
  # 自定义权限
  path('auth3/', views.Demo3APIView.as_view()),
  # 限流
  path('auth4/', views.Demo4APIView.as_view()),
]

opt下的views.py

# 限流
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle


class Demo4APIView(APIView):
  # throttle_classes = [UserRateThrottle, AnonRateThrottle] # 全局配置后,这里就不用指定

  def get(self, request):
    """投票页面"""
    return Response("投票页面")

4. 过滤Filtering

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

pip3 install django-filter

在配置文件里进行注册

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

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

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

opt下的urls.py

urlpatterns = [
  path('auth1/', views.Demo1APIView.as_view()),
  path('auth2/', views.Demo2APIView.as_view()),
  # 自定义权限
  path('auth3/', views.Demo3APIView.as_view()),
  # 限流
  path('auth4/', views.Demo4APIView.as_view()),
  # 过滤
  path('data5/', views.Demo5APIView.as_view()),
]

opt下的views.py

# 过滤
from rest_framework.generics import GenericAPIView, ListAPIView
from students.models import Student
from .serializers import StudentModelSerializer
from django_filters.rest_framework import DjangoFilterBackend


class Demo5APIView(ListAPIView):
  queryset = Student.objects.all()
  serializer_class = StudentModelSerializer
  filter_backends = [DjangoFilterBackend] # 全局配置后,这里就不用指定了。
  filter_fields = ['age', "id"] # 声明过滤字段

5. 排序Ordering

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

使用方法:

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

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

opt下的urs.py

urlpatterns = [
  path('auth1/', views.Demo1APIView.as_view()),
  path('auth2/', views.Demo2APIView.as_view()),
  # 自定义权限
  path('auth3/', views.Demo3APIView.as_view()),
  # 限流
  path('auth4/', views.Demo4APIView.as_view()),
  # 过滤
  path('data5/', views.Demo5APIView.as_view()),
  # 排序
  path('data6/', views.Demo6APIView.as_view()),
]

opt下的views.py

# 排序
from rest_framework.filters import OrderingFilter


class Demo6APIView(ListAPIView):
  queryset = Student.objects.all()
  serializer_class = StudentModelSerializer
  filter_backends = [DjangoFilterBackend, OrderingFilter] # 局部配置会覆盖全局配置
  filter_fields = ['id', "sex"]
  ordering_fields = ['id', "age"]

6. 分页Pagination

REST framework提供了分页的支持。

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

REST_FRAMEWORK = {
  'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
  'PAGE_SIZE': 100 # 每页数目
}

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

opt下的urls.py

urlpatterns = [
  path('auth1/', views.Demo1APIView.as_view()),
  path('auth2/', views.Demo2APIView.as_view()),
  # 自定义权限
  path('auth3/', views.Demo3APIView.as_view()),
  # 限流
  path('auth4/', views.Demo4APIView.as_view()),
  # 过滤
  path('data5/', views.Demo5APIView.as_view()),
  # 排序
  path('data6/', views.Demo6APIView.as_view()),
  # 分页
  path('data7/', views.Demo7APIView.as_view()),
]

opt下的views.py

# 分页
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination

"""1. 自定义分页器,定制分页的相关配置"""
"""
# 页码分页 PageNumberPagination
前端访问形式:GET http://127.0.0.1:8000/opt/data7/?page=4

page=1  limit 0,10
page=2  limit 10,20

# 偏移量分页 LimitOffsetPagination
前端访问形式:GET http://127.0.0.1:8000/opt/data7/?start=4&size=3

start=0 limit 0,10
start=10 limit 10,10
start=20 limit 20,10
"""


class StandardPageNumberPagination(PageNumberPagination):
  """分页相关配置"""
  page_query_param = "page"     # 设置分页页码关键字名
  page_size = 3           # 设置每页显示数据条数
  page_size_query_param = "size"   # 设置指定每页大小的关键字名
  max_page_size = 5         # 设置每页显示最大值


class StandardLimitOffsetPagination(LimitOffsetPagination):
  default_limit = 2         # 默认限制,默认值与PAGE_SIZE设置一致
  limit_query_param = "size"     # limit参数名
  offset_query_param = "start"    # offset参数名
  max_limit = 5           # 最大limit限制


class Demo7APIView(ListAPIView):
  queryset = Student.objects.all()
  serializer_class = StudentModelSerializer
  # 分页
  # 页码分页类
  pagination_class = StandardPageNumberPagination
  # 偏移量分页类
  # pagination_class = StandardLimitOffsetPagination

注意:如果在视图内关闭分页功能,只需在视图内设置

pagination_class = None

到此这篇关于Django DRF路由与扩展功能的实现的文章就介绍到这了,更多相关Django DRF路由与扩展内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python 3.x 连接数据库示例(pymysql 方式)
Jan 19 Python
python编程实现12306的一个小爬虫实例
Dec 27 Python
Python中的函数作用域
May 07 Python
python爬虫的数据库连接问题【推荐】
Jun 25 Python
Python设计模式之状态模式原理与用法详解
Jan 15 Python
Python批量生成特定尺寸图片及图画任意文字的实例
Jan 30 Python
python中selenium操作下拉滚动条的几种方法汇总
Jul 14 Python
Tensorflow设置显存自适应,显存比例的操作
Feb 03 Python
python GUI库图形界面开发之PyQt5线程类QThread详细使用方法
Feb 26 Python
python实现图像拼接
Mar 05 Python
Python浮点型(float)运算结果不正确的解决方案
Sep 22 Python
python3实现语音转文字(语音识别)和文字转语音(语音合成)
Oct 14 Python
Django中使用Json返回数据的实现方法
Jun 03 #Python
浅谈numpy中np.array()与np.asarray的区别以及.tolist
Jun 03 #Python
基于Python绘制美观动态圆环图、饼图
Jun 03 #Python
利用OpenCV中对图像数据进行64F和8U转换的方式
Jun 03 #Python
浅谈python opencv对图像颜色通道进行加减操作溢出
Jun 03 #Python
python golang中grpc 使用示例代码详解
Jun 03 #Python
Opencv图像处理:如何判断图片里某个颜色值占的比例
Jun 03 #Python
You might like
?算你??的 PHP 程式大小
2006/12/06 PHP
php下图片文字混合水印与缩略图实现代码
2009/12/11 PHP
curl不使用文件存取cookie php使用curl获取cookie示例
2014/01/26 PHP
CI框架源码解读之利用Hook.php文件完成功能扩展的方法
2016/05/18 PHP
PHP错误处理函数register_shutdown_function使用示例
2017/07/03 PHP
PHP实现深度优先搜索算法(DFS,Depth First Search)详解
2017/09/16 PHP
PHP实现双链表删除与插入节点的方法示例
2017/11/11 PHP
js对象数组按属性快速排序
2011/01/31 Javascript
ExtJs Excel导出并下载IIS服务器端遇到的问题
2011/09/16 Javascript
使用JavaScript实现Java的List功能(实例讲解)
2013/11/07 Javascript
javascript 通用loading动画效果实例代码
2014/01/14 Javascript
javascript字母大小写转换的4个函数详解
2014/05/09 Javascript
使用text方法获取Html元素文本信息示例
2014/09/01 Javascript
JQuery移动页面开发之屏幕方向改变与滚屏的实现
2015/12/03 Javascript
Bootstrap表单制作代码
2017/03/17 Javascript
express express-session的使用小结
2018/12/12 Javascript
vue实现压缩图片预览并上传功能(promise封装)
2019/01/10 Javascript
Django模板继承 extend标签实例代码详解
2019/05/16 Javascript
40行代码把Vue3的响应式集成进React做状态管理
2020/05/20 Javascript
Python中random模块生成随机数详解
2016/03/10 Python
Python实现的矩阵类实例
2017/08/22 Python
CentOS 6.5中安装Python 3.6.2的方法步骤
2017/12/03 Python
简单实现Python爬取网络图片
2018/04/01 Python
pandas.dataframe中根据条件获取元素所在的位置方法(索引)
2018/06/07 Python
对pandas的行列名更改与数据选择详解
2018/11/12 Python
浅谈pycharm的xmx和xms设置方法
2018/12/03 Python
Python编程深度学习绘图库之matplotlib
2018/12/28 Python
对Python多线程读写文件加锁的实例详解
2019/01/14 Python
Python 多线程共享变量的实现示例
2020/04/17 Python
Athleta官网:购买女士瑜伽服、技术运动服和休闲运动服
2020/11/12 全球购物
大学生四年生活自我鉴定
2013/11/21 职场文书
腾讯广告词
2014/03/19 职场文书
2014高中生入党思想汇报范文
2014/09/13 职场文书
高校师德师风自我剖析材料
2014/09/29 职场文书
幼儿园教师师德承诺书
2015/04/28 职场文书
幼儿园小班教师随笔
2015/08/14 职场文书