Python笔记_63_Django-DRF_视图集与路由的使用_扩展功能

Django-DRF(路由与扩展功能)

视图集与路由的使用

在应用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"]
ViewSet
使用视图集ViewSet,可以将一系列逻辑相关的动作放到一个类中:

list() 提供一组数据

retrieve() 提供单个数据

create() 创建数据

update() 保存数据

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

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

  • collect/views.py
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):
        """获取5条数据"""
        student_list = Student.objects.all()[:5]

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

        return Response(serializer.data)

    def get_one(self, request, pk):
        """获取一条数据"""
        student_obj = Student.objects.get(pk=pk)

        serializer = StudentModelSerializer(instance=student_obj)

        return Response(serializer.data)

    def get_5_girl(self, request):
        """获取5条女孩数据"""
        student_list = Student.objects.filter(sex=False)[:5]

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

        return Response(serializer.data)
  • 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_girl/", views.Student1ViewSet.as_view({"get": "get_5_girl"})),
    re_path(r"^student1/(?P<pk>\d+)/$", views.Student1ViewSet.as_view({"get": "get_one"})),
 ]
GenericViewSet

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

  • collect/views.py
from rest_framework.viewsets import GenericViewSet


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

    def get_5(self, request):
        """获取5条数据"""
        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):
        """获取5条女孩数据"""
        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.mixins import ListModelMixin, CreateModelMixin

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

    # 注意: 我们这里使用的是视图集, 可以在路由中指定方法,执行的方法
  • collect/urls.py
from django.urls import path, re_path
from collect import views

urlpatterns = [
    # GenericViewSet
    path('student2/', views.Student3ViewSet.as_view({"get": "get_5"})),
    path('student2/get_girl/', views.Student3ViewSet.as_view({"get": "get_5_girl"})),
    # GenericViewSet,可以和模型类进行组合快速生成基本的API接口
    path('student3/', views.Student4ViewSet.as_view({"get": "list", "post": "create"})),
 ]
ModelViewSet

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

  • collect/views.py
from rest_framework.viewsets import ModelViewSet


class Student5ViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
  • collect/urls.py
from django.urls import path, re_path
from collect import views

urlpatterns = [
    # ModelViewSet 默认提供了5个API接口
    path('student4/', views.Student5ViewSet.as_view({"get": "list", "post": "create"})),
    re_path(r"^student4/(?P<pk>\d+)/$", views.Student5ViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
ReadOnlyModelViewSet

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

  • collect/views.py
from rest_framework.viewsets import ReadOnlyModelViewSet


class Student6ViewSet(ReadOnlyModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
  • collect/urls.py
from django.urls import path, re_path
from collect import views

urlpatterns = [
        # ReadOnlyModelViewSet
    path('student5/', views.Student6ViewSet.as_view({'get': 'list'})),
    re_path(r"^student5/(?P<pk>\d+)/$", views.Student6ViewSet.as_view({"get": "retrieve"})),
合并路由

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

  • collect/views.py
    • methods 指定允许哪些http请求访问当前视图方法
    • detail 指定生成的路由地址中是否要夹带pk值,True为需要
class Student7ViewSet(ModelViewSet):
    queryset = Student.objects.all()
    serializer_class = StudentModelSerializer
    
    @action(methods=['get', "post"], detail=True)
    def get_6(self, request, pk):
       
        student_obj = self.get_queryset().get(pk=pk)
        serilizer = self.get_serializer(instance=student_obj)
        return Response(serilizer.data)
  • collect/urls.py
from rest_framework.routers import DefaultRouter, SimpleRouter

# 实例化router对象
router = DefaultRouter()  # 会生成api-root
# router = SimpleRouter()  # 不会生成api-root

# router.register("访问地址前缀","视图集类","访问别名[可选]")
# 注册视图视图集类

router.register("student7", views.Student7ViewSet)  # url后缀不要加/

# print(router.urls)
## < URLPattern  '^student7/(?P<pk>[^/.]+)/get_6/$'[name = 'student-get-6'] >,
## < URLPattern  '^student7/(?P<pk>[^/.]+)/get_6\.(?P<format>[a-z0-9]+)/?$'[name = 'student-get-6'] >,


# 把路由列表注册到django项目中
urlpatterns += router.urls
调用多个序列化器

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

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

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

    # GenericAPI内部调用序列化器的方法,我们可以重写这个方法来实现根据不同的需求来调用不同的序列化器
    def get_serializer_class(self):
        if self.request.method == "GET":
            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)
  • collect/urls.py
from django.urls import path, re_path
from collect import views


urlpatterns = [
	# 一个视图类调用多个序列化器类
    path("student8/", views.Student8GenericAPIView.as_view()),
    ]
在一个视图集中调用多个序列化器
  • collect/views.py
class Student9GenericAPIView(ModelViewSet):
    queryset = Student.objects.all()
    """要求:
            列表数据list,返回2个字段,
            详情数据retrieve,返回所有字段,
    """

    def get_serializer_class(self):
        # print(self.action)  # 当前请求要执行的方法名称
        if self.action == "list":
            return StudentInfoModelSerializer
        return StudentModelSerializer
  • collect/urls.py
from django.urls import path, re_path
from collect import views


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

扩展功能

创建一个新的子应用 opt
用django内置admin站点并创建一个管理员.

认证Authentication

可以在配置文件drf_demo/settings.py中配置全局默认的认证方案

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

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

  • opt/views.py
"""用户的认证和权限识别"""

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")
  • 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()),
    ]
权限Permissions

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

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

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

内置提供的权限:

AllowAny 允许所有用户

IsAuthenticated 仅通过认证的用户

IsAdminUser 仅管理员用户

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

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

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

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

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

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

  • 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")
  • opt/urls.py
from django.urls import path
from opt import views

urlpatterns = [
    # 自定义权限
    path('auth3/', views.Demo3APIView.as_view()),
    ]
限流Throttling

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

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

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

REST_FRAMEWORK = {
    # 限流
    'DEFAULT_THROTTLE_CLASSES': (  # 对全局进行设置
        'rest_framework.throttling.AnonRateThrottle',
        'rest_framework.throttling.UserRateThrottle'
    ),
    'DEFAULT_THROTTLE_RATES': {
        'anon': '3/hour',  # 可以使用 second, minute, hour 或day来指明周期。
        'user': '3/minute',
    }
}

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

  • opt/views.py
# 限流
from rest_framework.throttling import UserRateThrottle, AnonRateThrottle


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

    def get(self, request):
        """投票页面"""
        return Response("投票页面")
  • opt/urls.py
from django.urls import path
from opt import views

urlpatterns = [
    # 限流
    path('auth4/', views.Demo4APIView.as_view()),
    ]
过滤Filtering

对于列表数据可能需要根据字段进行过滤,我们可以通过添加django-fitlter扩展来增强支持。pip3 install django-filter
  在配置文件里进行注册

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

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

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

  • 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"]  # 声明过滤字段
  • opt/urls.py
from django.urls import path
from opt import views

urlpatterns = [
    # 过滤
    path('data5/', views.Demo5APIView.as_view()),
    ]
排序Ordering

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

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

  • 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', "age", "sex"]
    ordering_fields = ['id', "age"]
  • opt/urls.py
from django.urls import path
from opt import views

urlpatterns = [
     # 排序
    path('data6/', views.Demo6APIView.as_view()),
    ]
分页Pagination

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

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

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

  1. 页码分页 PageNumberPagination
    前端访问形式:GET http://127.0.0.1:8000/opt/data7/?page=4
    page=1 limit 0,10
    page=2 limit 10,20

  2. 偏移量分页 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

  • opt/views.py
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
  • opt/urls.py
from django.urls import path
from opt import views

urlpatterns = [
     # 分页
    path('data7/', views.Demo7APIView.as_view()),
    ]

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值