【DRF】APIView、GenericAPIView、扩展类ListModelMixin、子类视图ListAPIView、ModelViewSet的用法

本文介绍了DjangoREST框架中APIView和GenericAPIView的使用,包括模型、序列化、增删改查操作。同时,讨论了过滤、排序和分页功能的实现,以及ModelViewSet的使用,展示了如何通过这些工具构建API视图。
摘要由CSDN通过智能技术生成

前言

记录下这些view的使用,方便以后查阅

Model

from django.db import models
from django.conf import settings
from utils.models import BaseModel

class Projects(BaseModel):
    id = models.AutoField(primary_key=True,verbose_name='id主键',help_text='id主键')
    name = models.CharField('项目名称', max_length=40, unique=True, help_text='项目名称')
    leader = models.ForeignKey(to = settings.AUTH_USER_MODEL, verbose_name='负责人',
                               null=True, on_delete=models.SET_NULL, related_name='projects_leader')
    desc = models.CharField('项目描述', max_length=200, null=True, blank=True, default='',
                            help_text='项目描述')

    class Meta:
        db_table = 'tb_projects'
        verbose_name = '项目信息'
        verbose_name_plural = verbose_name
        ordering = ['-c_time']

    def __str__(self):
        return self.name

Serializers

from rest_framework import serializers
from projects.models import Projects


class ProjectModelSerializer(serializers.ModelSerializer):

    email = serializers.EmailField(write_only=True)
    leader = serializers.ReadOnlyField(source='leader.username')

    def validate_name(self, value):
        value: str
        if not value.endswith('项目'):
            raise serializers.ValidationError('项目名称必须得以“项目”结尾')
        return value


    def create(self, validated_data: dict):
        validated_data.pop('email')
        obj = Projects.objects.create(**validated_data)
        return obj


    def update(self, instance, validated_data):
        instance.name = validated_data.get('name') 
        instance.save()
        return instance 

    class Meta:
        model = Projects
        exclude = ['is_delete', 'u_time']

一、APIView

  1. 继承APIView的视图类具备View的所有特性
  2. 提取请求字符串参数的request.query_parms方法,可以获取到前端的查询字符串参数
  3. 请求体参数的request.data方法,可以获取到请求体参数
  4. APIView为视图类提供了认证,授权,限流等功能
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from projects.models import Projects
from projects.serializers import ProjectModelSerializer



class ProjectsViews(APIView):

    # 获取数据库所有数据
    def get(self,request):
        # 从数据库查询出所有的数据
        project_list = Projects.objects.all()
        # 使用serializers里定义的序列化器对库里查出来的数据进行序列化,instance: 要序列化的对象,就是从库里查出来的数据
        # many: 如果是查询集对象,many=True;如果是单个对象,many=False
        serializer = ProjectModelSerializer(instance=project_list,many=True)
        # 将序列化后的数据返回给前端,serializer.data序列化后是字典
        return Response(serializer.data)

    # 新增
    def post(self,request):
        # 获取前端传过来的数据,字典格式传入data进行反序列化,创建序列化器对象
        serializer = ProjectModelSerializer(data=request.data)
        # 校验数据
        if serializer.is_valid(): # 如果是TRUE,表示数据校验通过,通过,就保存
            # 如果instance为None,调用save本质会调用create--》父类create直接抛异常,所以我们要重写
            # 序列化器对象调用save方法时,会调用序列化器类中的create方法,进行数据创建操作
            serializer.save()  # 就会保存,重写create方法,如果不重写,我们不知道存到哪个表中,前提是使用的serializer,不是modelserializer
            # return Response(serializer.data)
            return Response(serializer.data, status=status.HTTP_200_OK)
        return Response({'code':101,'msg':'数据校验失败','err':serializer.errors})

class ProjectsDetailViews(APIView):

    # 获取数据库单条数据
    def get(self,requet,pk):
        project_detail_list = Projects.objects.filter(pk=pk).first()
        serializer = ProjectModelSerializer(instance=project_detail_list)
        if project_detail_list:
            return Response(serializer.data)
        else:
            return Response({'code':103,'msg':'数据不存在'}, status=status.HTTP_204_NO_CONTENT)

    # 修改单条数据
    def put(self,request,pk):
        # 用什么数据,修改哪条数据
        project_update_one = Projects.objects.filter(pk=pk).first()
        # 进行序列化,既有instance又有data,表示修改
        serializer = ProjectModelSerializer(instance=project_update_one,data=request.data)
        # 校验数据
        if serializer.is_valid():
            serializer.save() # 重写update方法
            return Response(serializer.data)
        return Response({'code':102,'msg':'修改出错','err':serializer.errors})

    # 删除
    def delete(self,request,pk):
        # 先获取要删除哪条数据
        project_del_one = Projects.objects.filter(pk=pk).delete()
        if project_del_one[0]>0:
            return Response({'code':100,'msg':'删除成功'}, status=status.HTTP_204_NO_CONTENT)
        else:
            return Response({'code':103,'msg':'数据不存在'}, status=status.HTTP_204_NO_CONTENT)

二、GenericAPIView

  • 具备View的所有特性
  • 具备APIView中的认证,授权,限流功能
  • 支持对于获取列表数据接口的过滤,排序,分页功能
  • 一旦继承了GenericAPIView,往往需要指定queryset,serializer_class类属性

过滤

  • 安装django-filter : pip install django-filter
  • settings中进行配置
INSTALL_APPS = {
...
'django_filters',
...
}
REST_FRAMEWORK = {
# 通用配置,所有的视图默认使用 #全局配置,对全部视图的查询多条接口都有过滤功能,但是视图里要指定字段
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
  • 访问127.0.0.1/employees?gender=1时可以过滤出性别字段是1的数据。
class EmployeeViewSet(ModelViewSet):
    queryset = Employee.objects.all()
    serializer_class = EmployeeSerializer
    # 指定可以根据哪些字段进行列表数据的过滤
    filterset_fields = ('gender', 'department')
# 单独视图配置
from django_filters.rest_framework import DjangoFilterBackend
class SomeVIew(generics.xxxAPIView):
    ...
      # 指定过滤器
    fitler_backends = [DjangoFilterBackend]
     # 指定可以根据哪些字段进行列表数据的过滤
      filterset_fields = ['project', 'project__name']

排序

  • 访问127.0.0.1/employees/?ordering=age(年龄升序)
  • 访问127.0.0.1/employees/?ordering=-age(年龄降序)
  • 访问127.0.0.1/employees/?ordering=age,-salary(先安装年龄升序来排,如果年龄一样,按工资升序来排)
class EmployeeViewSet(ModelViewSet):

    queryset = Employee.objects.all()
    serializer_class = EmployeeSerializer

    # 新增排序的过滤器
    filter_backends = [..., OrderingFilter]
    # 指定可以根据哪此字段进行排序
    ordering_fields = ('age', 'salary')

分页

  • 在settings中配置
REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS':  'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 2  # 每页显示多少条数据
}

定义完分页器后,可以在某个视图里面进行使用:

class DepartmentViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet):
    ...
    # 指定分页配置
    pagination_class = MyPageNumberPagination
    ...

如果url是127.0.0.1/department?page=3&page_size=5,代表打开第3页,每一页显示5条数据。

  • 自定义分页器
class MyPageNumberPagination(PageNumberPagination):    
    # 指定默认每一页显示的数据条数
    page_size = 3
    # 前端用于指定页号的查询字符串参数名称
    page_query_param = 'page'
    # 指定前端用于指定页号的查询字符串参数的描述
    page_query_description = '获取的页码'
    # page_size_query_param = "page_size"
    # 前端用于指定每一页的数据条数,查询字符串参数名称
    # 只要设置了page_size_query_param,那么前端就支持指定获取每一页的数据条数
    page_size_query_param = "size"
    # 前端用于指定每一页的数据条数,查询字符串参数的描述
    page_size_query_description = '每一页数据条数'
    max_page_size = 100
    
    def get_paginated_response(self, data):
        response = super().get_paginated_response(data)
        response.data['current_page_num'] = self.page.number
        response.data['total_pages'] = self.page.paginator.num_pages
        return response
from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from projects.models import Projects
from projects.serializers import ProjectModelSerializer


class ProjectsViews(GenericAPIView):
    # 需要指定queryset和serializer_class
    queryset = Projects.objects.all()
    serializer_class = ProjectModelSerializer
    # 指定可以根据哪些字段进行列表数据的过滤 ?name=
    filterset_fields = ('name', 'leader')
    # 需要新增排序的过滤器
    filter_backends = [DjangoFilterBackend,OrderingFilter]
    # 指定可以根据哪此字段进行排序
    ordering_fields = ('id')
    # 分页
    pagination_class = PageNumberPagination

    def get(self, request):
        # 直接从数据库取出来数据进行了序列化
        serializer = self.get_serializer(instance=self.get_queryset(), many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)

    def post(self,request):
        serializer = self.get_serializer(data = request.data)
        serializer.is_valid(raise_exception = True)
        serializer.save()
        return Response(serializer.data,status = status.HTTP_201_CREATED)

class ProjectsDetailViews(GenericAPIView):
    queryset = Projects.objects.all()
    serializer_class = ProjectModelSerializer

    def get(self,request):
        serializer = self.get_serializer(instance = self.get_object())
        return Response(serializer.data,status=status.HTTP_200_OK)

    def put(self,request):
        serializer = self.get_serializer(instance = self.get_object(),data = request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)

    def delete(self,request):
        self.get_object().delete()
        return Response({'code':1003,'msg':'数据删除成功'},status=status.HTTP_204_NO_CONTENT)

具备的类属性和方法
类属性:

queryset = None
serializer_class = None
lookup_field = ‘pk’
lookup_url_kwarg = None
filter_backends =api_settings.DEFAULT_FILTER_BACKENDS
pagination_class =api_settings.DEFAULT_PAGINATION_CLASS

  • queryset

queryset是用来控制视图返回给前端的数据。如果没什么逻辑,可以直接写在视图的类属性中,如果逻辑比较复杂,也可以重写get_queryset方法用来返回一个queryset对象。如果重写了get_queryset,那么以后获取queryset的时候就需要通过调用get_queryset方法。因为queryset这个属性只会调用一次,以后所有的请求都是使用他的缓存。

  • serializer_class

当前类视图的内部的实例方法会用到的序列化器类

  • lookup_field

在检索的时候,根据什么参数进行检索。默认是pk,也就是主键。

  • lookup_url_kwarg

在检索的url中的参数名称。默认没有设置,跟lookup_field保持一致。

  • filter_backends

用于过滤查询集的过滤器后端类的列表。默认值与DEFAULT_FILTER_BACKENDS 设置的值相同。

  • pagination_class

当分页列出结果时应使用的分页类。默认值与DEFAULT_PAGINATION_CLASS 设置的值相同,即 ‘rest_framework.pagination.PageNumberPagination’。

方法:

  1. get_queryset(self)

默认返回由 queryset 属性指定的查询集。默认是返回数据库全部数据,如果想返回其他数据,需要自定义。


def get_queryset(self):
    assert self.queryset is not None, (
        "'%s' should either include a `queryset` attribute, "
        "or override the `get_queryset()` method."
        % self.__class__.__name__
    )
    queryset = self.queryset  # 设置queryset等于一个查询集,这里的queryset等于self的queryset
    if isinstance(queryset, QuerySet): # 判断queryset是不是查询集类型
        queryset = queryset.all() # 是的话就获取所有数据,然后返回
    return queryset
  1. get_object(self)

返回详情pk视图所需的模型类数据对象。默认使用 lookup_field 参数过滤基本的查询集。

def get_object(self):
    
    queryset = self.filter_queryset(self.get_queryset())

    # Perform the lookup filtering.
    lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field

    assert lookup_url_kwarg in self.kwargs, (
        'Expected view %s to be called with a URL keyword argument '
        'named "%s". Fix your URL conf, or set the `.lookup_field` '
        'attribute on the view correctly.' %
        (self.__class__.__name__, lookup_url_kwarg)
    )

    filter_kwargs = {self.lookup_field: self.kwargs[lookup_url_kwarg]}
    obj = get_object_or_404(queryset, **filter_kwargs)

    # May raise a permission denied
    self.check_object_permissions(self.request, obj)

    return obj
    
    返回具体pk值的数据
    
    def get_object(self,pk):
    try:
        return Projects.objects.get(id=pk)
    except Exception:
        raise Http404
  1. get_serializer(self, instance=None, data=None, many=False, partial=False)

返回一个序列化器的实例,不需要重写get_serializer()方法,直接调用即可

def get_serializer(self, *args, **kwargs):

    serializer_class = self.get_serializer_class()  # 调用上面的方法获取serializer_class属性再返回
    kwargs.setdefault('context', self.get_serializer_context())
    return serializer_class(*args, **kwargs)
  1. get_serializer_class(self)

返回应用于序列化的类。默认为返回 serializer_class 属性的值。如果您需要根据传入请求提供不同的序列化,您可能需要重写它。

def get_serializer_class(self):

    assert self.serializer_class is not None, (
        "'%s' should either include a `serializer_class` attribute, "
        "or override the `get_serializer_class()` method."
        % self.__class__.__name__
    )

    return self.serializer_class  # 返回一个serializer_class,模型序列化器
  1. filter_queryset(self)

默认不需要过滤,如果需要过滤需要重写
filter_queryset()方法

def filter_queryset(self, queryset):
        """
        Given a queryset, filter it with whichever filter backend is in use.

        You are unlikely to want to override this method, although you may need
        to call it either from a list view, or from a custom `get_object`
        method if you want to apply the configured filtering backend to the
        default queryset.
        """
        for backend in list(self.filter_backends):
            queryset = backend().filter_queryset(self.request, queryset, self)
        return queryset
  1. paginate_queryset(self)

a、默认不需要分页,如果需要分页需要重写paginate_queryset()方法
b、如果self.paginator 为 None,不需要进行过滤,否则需要通过self.paginator.paginate_queryset()方法进行过滤

def paginate_queryset(self, queryset):
        """
        Return a single page of results, or `None` if pagination is disabled.
        """
        if self.paginator is None:
            return None
        return self.paginator.paginate_queryset(queryset, self.request, view=self)

三、五个扩展类

扩展类作用封装的方法状态码(成功、失败)
ListModelMixin查询多条数据list200
CreateModelMixin新增一条数据create201/400
RetrieveModelMixin查询一条数据retrieve200/404
UpdateModelMixin更新一条数据update/partial_update200/400
DestroyModelMixin删除一条数据destroy204/404
from rest_framework.filters import OrderingFilter
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, \
    DestroyModelMixin
from projects.models import Projects
from projects.serializers import ProjectModelSerializer


class ProjectsViews(ListModelMixin, CreateModelMixin, GenericAPIView):
    # 需要指定queryset和serializer_class
    queryset = Projects.objects.all()
    serializer_class = ProjectModelSerializer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)


class ProjectsDetailViews(RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, GenericAPIView):
    queryset = Projects.objects.all()
    serializer_class = ProjectModelSerializer

    def get(self, request, pk):
        return self.retrieve(request, pk)

    def post(self, request, pk):
        return self.update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)

四、子类视图

视图作用方法
ListAPIView查询多条数据get
CreateAPIView新增一条数据post
RetrieveAPIView查询一条数据get
UpdateAPIView更新一条数据put/patch
DestroyAPIView删除一条数据delete
RetrieveUpdateAPIView查询,更新一条数据get/put/patch
RetrieveUpdateDestroyAPIView获取,更新,删除一条数据get/put/patch/delete
class DepartmentListAPIView(ListAPIView,CreateAPIView):
    ”“”实现了查询多条数据和新增一条数据的功能"""
    queryset = Department.objects.all()
    serializer_class = DepartmentSerializer


class DepartmentDetailAPIView(RetrieveUpdateDestroyAPIView):
    """实现了查询一条数据、修改一条数据、删除一条数据的功能"""
    queryset = Department.objects.all()
    serializer_class = DepartmentSerializer

五、视图集ModelViewSet

  • 继承自GenericViewSet
  • 包括了 ListModelMixin、RetrieveModelMixin、CreateModelMixin、UpdateModelMixin、DestroyModelMixin
class ProjectsViews(ModelViewSet):
    # 需要指定queryset和serializer_class
    queryset = Projects.objects.all()
    serializer_class = ProjectModelSerializer

路由

urlpatterns = [
    path('projects', views.ProjectsViews.as_view({'get':'list','post':'create'})),
    path('projects/<int:pk>', views.ProjectsDetailViews.as_view({
        'get':'retrieve','put':'update','delete':'destroy'
    }))
]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值