DRF知识点总结(2)

简介: DRF知识点总结(2)

七、 Serializer*

drf中为我们提供了Serializer,他主要有两大功能:

  • 对请求数据校验(底层调用Django的Form和ModelForm)
  • 对数据库查询到的对象进行序列化

我们新建一个项目用来学习serializer(此时没有认证权限什么的了)。

建表如下:

# models.py
from django.db import models
class Role(models.Model):
    """ 角色表 """
    title = models.CharField(verbose_name="名称", max_length=32)
class Department(models.Model):
    """ 部门表 """
    title = models.CharField(verbose_name="名称", max_length=32)
class UserInfo(models.Model):
    """ 用户表 """
    level_choices = ((1, "普通会员"), (2, "VIP"), (3, "SVIP"),)
    level = models.IntegerField(verbose_name="级别", choices=level_choices, default=1)
    username = models.CharField(verbose_name="用户名", max_length=32)
    password = models.CharField(verbose_name="密码", max_length=64)
    age = models.IntegerField(verbose_name="年龄", default=0)
    email = models.CharField(verbose_name="邮箱", max_length=64)
    token = models.CharField(verbose_name="TOKEN", max_length=64, null=True, blank=True)
    # 外键
    depart = models.ForeignKey(verbose_name="部门", to="Department", on_delete=models.CASCADE)
    # 多对多
    roles = models.ManyToManyField(verbose_name="角色", to="Role")

1. 校验数据

a. 继承Serializer

import re
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from app01 import models
from rest_framework import exceptions
from django.core.validators import EmailValidator
class RegexValidator:
    def __init__(self, base):
        self.base = str(base)
    def __call__(self, value):
        match_object = re.match(self.base, value)
        if not match_object:
            raise serializers.ValidationError("格式错误")
class UserSerializer(serializers.Serializer):
    username = serializers.CharField(label="姓名", min_length=6, max_length=32)
    age = serializers.IntegerField(label="年龄", min_value=0, max_value=200)
    level = serializers.ChoiceField(label="级别", choices=models.UserInfo.level_choices)
    email = serializers.CharField(label="邮箱", validators=[EmailValidator, ])
    email1 = serializers.EmailField(label="邮箱1", min_length=6, max_length=32)
    email2 = serializers.CharField(label="邮箱2", validators=[RegexValidator(r"^\w+@\w+\.\w+$"), ])
    email3 = serializers.CharField(label="邮箱3", min_length=6, max_length=32)
    def validate_email3(self, value):
        if re.match(r"^\w+@\w+\.\w+$", value):
            return value
        raise exceptions.ValidationError("邮箱格式错误")
class UserView(APIView):
    def post(self, request, *args, **kwargs):
        ser = UserSerializer(data=request.data, )
        print(ser)
        print(ser.is_valid())
        if not ser.is_valid():
            return Response({"code": 1006, "data": ser.errors})
        print(ser.validated_data)
        return Response({'code': 1000, 'data': '创建成功'})
'''
UserSerializer(data=<QueryDict: {'username': ['suichu'], 'password': ['xxx'], 'age': ['18'], 'level': ['3'], 'email': ['xxx@qq.com'], 'email1': ['xxx@qq.com'], 'email2': ['xxx@qq.com'], 'email3': ['xxx@qq.com'], '': ['']}>):
    username = CharField(label='姓名', max_length=32, min_length=6)
    age = IntegerField(label='年龄', max_value=200, min_value=0)
    level = ChoiceField(choices=((1, '普通会员'), (2, 'VIP'), (3, 'SVIP')), label='级别')
    email = CharField(label='邮箱', validators=[<class 'django.core.validators.EmailValidator'>])
    email1 = EmailField(label='邮箱1', max_length=32, min_length=6)
    email2 = CharField(label='邮箱2', validators=[<app01.views.RegexValidator object>])
    email3 = CharField(label='邮箱3', max_length=32, min_length=6)
True
OrderedDict([('username', 'suichu'), ('age', 18), ('level', 3), ('email', 'xxx@qq.com'), ('email1', 'xxx@qq.com'), ('email2', 'xxx@qq.com'), ('email3', 'xxx@qq.com')])
'''

注:UserSerializer中有的字段,默认传参时都必须要有。如果传过来了一些没有的键值对也没关系,其内部只会对有的进行匹配对比认证。而ser.validated_data返回的仅是匹配字段的数据。


将settings里语言修改则可以看到中文的回复


LANGUAGE_CODE = 'zh-hans'

b. 继承ModelSerializer

import re
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import serializers
from app01 import models
from rest_framework import exceptions
from django.core.validators import EmailValidator
class RegexValidator:
    def __init__(self, base):
        self.base = str(base)
    def __call__(self, value):
        match_object = re.match(self.base, value)
        if not match_object:
            raise serializers.ValidationError("格式错误")
class UserSerializer(serializers.ModelSerializer):
    # 字段补充
    email2 = serializers.CharField(label="邮箱2", validators=[RegexValidator(r"^\w+@\w+\.\w+$"), ])
    email3 = serializers.CharField(label="邮箱3", min_length=6, max_length=32)
    class Meta:
        # 表声明
        model = models.UserInfo
        # 表中字段声明
        fields = ['username', 'age', 'email', 'email2', 'email3', 'depart', 'roles']
        # 额外扩充的要求
        extra_kwargs = {
            'username': {"min_length": 6, "max_length": 32},
            "emali": {"validators": [EmailValidator]}
        }
        def validate_email3(self, value):
            if re.match(r"^\w+@\w+\.\w+$", value):
                return value
            raise exceptions.ValidationError("邮箱格式错误")
class UserView(APIView):
    def post(self, request, *args, **kwargs):
        ser = UserSerializer(data=request.data, )
        if not ser.is_valid():
            return Response({"code": 1006, "data": ser.errors})
        ser.validated_data.pop('email2')
        ser.validated_data.pop('email3')
        #  save有返回值的 是新增的那个对象
        ser.save(level=1, password='123')
        return Response({'code': 1000, 'data': '创建成功'})

可以看到,以username为例它需要长度在6-32(models里只限定了最大长度)。以age为例我虽然没有传age但是数据库里age有默认值0虽然写在fields里了但可以不传。以password为例我虽然传了password='xxx' 但最后保存的还是ser.save()里的password=123

2. 序列化

a. 序列化基本字段

通过ORM从数据库获取到的 QuerySet 或 对象 均可以被序列化为 json 格式数据。

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        # 表声明
        model = models.UserInfo
        # 表中字段声明
        fields = ['username', 'age', 'email']
class UserView(APIView):
    def get(self, request, *args, **kwargs):
        user_objs = models.UserInfo.objects.all()
        ser = UserSerializer(instance=user_objs, many=True)
        print(ser.data) 
        return Response({'code': 1000, 'data': ser.data})
# [OrderedDict([('username', 'suichu'), ('age', 0), ('email', 'xxx@qq.com')])]

如上图,ser.data返回OrderedDict,但通过Response后传给前台的就是json字符串。


# 切记, 如果从数据库获取的不是QuerySet对象,而是单一对象,例如:

data_object = modes.UserInfo.objects.filter(id=2).first()

ser = UserModelSerializer(instance=data_object,many=False)

print(ser.data)

b. 自定义字段

为方便展示我在数据库里又加了一条用户。

class UserSerializer(serializers.ModelSerializer):
    # choicefield中文显示
    level_text = serializers.CharField(source="get_level_display")
    # 一对多显示其title
    depart = serializers.CharField(source="depart.title")
    # 多对多 下面自定义
    roles = serializers.SerializerMethodField()
    # 额外新增字段  下面自定义
    extra = serializers.SerializerMethodField()
    class Meta:
        model = models.UserInfo
        fields = ['username', 'age', 'email', 'depart', 'roles', 'level_text', 'extra']
    def get_roles(self, obj):
        data_list = obj.roles.all()
        return [model_to_dict(item, ["id", "title"]) for item in data_list]
    def get_extra(self, obj):
        return 666
class UserView(APIView):
    def get(self, request, *args, **kwargs):
        user_objs = models.UserInfo.objects.all()
        ser = UserSerializer(instance=user_objs, many=True)
        print(ser.data)
        return Response({'code': 1000, 'data': ser.data})

c. 序列化字段嵌套

class DepartModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Department
        fields = "__all__"
class RoleModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Role
        fields = "__all__"
class UserSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(source="get_level_display")
    depart = DepartModelSerializer()
    roles = RoleModelSerializer(many=True)
    class Meta:
        model = models.UserInfo
        fields = ['username', 'age', 'email', 'depart', 'roles', 'level_text']
class UserView(APIView):
    def get(self, request, *args, **kwargs):
        user_objs = models.UserInfo.objects.all()
        ser = UserSerializer(instance=user_objs, many=True)
        print(ser.data)
        return Response({'code': 1000, 'data': ser.data})

3. 数据校验&序列化

上述示例均属于单一功能(要么校验,要么序列化),其实当我们编写一个序列化类既可以做数据校验,也可以做序列化,例如:

class DepartModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Department
        fields = ['id', "title"]
        extra_kwargs = {
            "id": {"read_only": False},  # 数据验证 即post时必须传递  在get时显示
            "title": {"read_only": True}  # 序列化 即在get时显示 post时不用传递
        }
class RoleModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Role
        fields = ['id', "title"]
        extra_kwargs = {
            "id": {"read_only": False},  # 数据验证
            "title": {"read_only": True}  # 序列化
        }
class UserModelSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(source="get_level_display", read_only=True)
    # Serializer嵌套,不是read_only,一定要自定义create和update,自定义新增和更新的逻辑。
    depart = DepartModelSerializer(many=False)
    roles = RoleModelSerializer(many=True)
    extra = serializers.SerializerMethodField(read_only=True)
    email2 = serializers.EmailField(write_only=True)
    # 数据校验:username、email、email2、部门、角色信息
    class Meta:
        model = models.UserInfo
        fields = [
            "username", "age", "email", "level_text", "depart", "roles", "extra", "email2"
        ]
        extra_kwargs = {
            "age": {"read_only": True},
            "email": {"validators": [EmailValidator, ]},
        }
    def get_extra(self, obj):
        return 666
    def validate_username(self, value):
        return value
    # 新增加数据时
    def create(self, validated_data):
        """ 如果有嵌套的Serializer,在进行数据校验时,只有两种选择:
              1. 将嵌套的序列化设置成 read_only
              2. 自定义create和update方法,自定义新建和更新的逻辑
            注意:用户端提交数据的格式。
        """
        depart_id = validated_data.pop('depart')['id']
        role_id_list = [ele['id'] for ele in validated_data.pop('roles')]
        # 新增用户表
        validated_data['depart_id'] = depart_id
        user_object = models.UserInfo.objects.create(**validated_data)
        # 在用户表和角色表的关联表中添加对应关系
        user_object.roles.add(*role_id_list)
        return user_object
class UserView(APIView):
    """ 用户管理 """
    def get(self, request):
        """ 添加用户 """
        queryset = models.UserInfo.objects.all()
        ser = UserModelSerializer(instance=queryset, many=True)
        return Response({"code": 0, 'data': ser.data})
    def post(self, request):
        """ 添加用户 """
        ser = UserModelSerializer(data=request.data)
        if not ser.is_valid():
            return Response({'code': 1006, 'data': ser.errors})
        ser.validated_data.pop('email2')
        instance = ser.save(age=18, password="123", depart_id=1)
        # 新增之后的一个对象(内部调用UserModelSerializer进行序列化)
        # 相当于 ser = UserModelSerializer(instance=instance, many=False)
        # ser.data
        print(instance)
        return Response({'code': 0, 'data': ser.data})

注:

传入数据时,必须是fields中全部字段(read_only、detault除外)

read_only只会在返回的时候给你展现出来(对应字段的内容/新增字段的内容),哪怕是你传递的时候带着read_only的数据也没什么用,它只匹配非read_only的字段。

对于返回时,返回的内容是校验器中所有非write_only的字段 ,而不是传进去的字段。

如果校验器中传参 partial = True,那么允许局部校验。


ser = UserModelSerializer(data=request.data,partial = True)

八、视图*

1. APIView

APIView,是drf中 “顶层” 的视图类,在他的内部主要实现drf基础的组件的使用 ,在请求到来时,新增了:免除csrf、请求封装、版本、认证、权限、限流的功能。

# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
    path('api/users/', views.UserView.as_view()),
    path('api/users/<int:pk>/', views.UserDetailView.as_view()),
]
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class UserView(APIView):
    # 认证、权限、限流等
    def get(self, request):
    # 业务逻辑:查看列表
        return Response({"code": 0, 'data': "..."})
    def post(self, request):
        # 业务逻辑:新建
        return Response({'code': 0, 'data': "..."})
class UserDetailView(APIView):
  # 认证、权限、限流等
    def get(self, request,pk):
    # 业务逻辑:查看某个数据的详细
        return Response({"code": 0, 'data': "..."})
    def put(self, request,pk):
        # 业务逻辑:全部修改
        return Response({'code': 0, 'data': "..."})
    def patch(self, request,pk):
        # 业务逻辑:局部修改
        return Response({'code': 0, 'data': "..."})
    def delete(self, request,pk):
        # 业务逻辑:删除
        return Response({'code': 0, 'data': "..."})

drf中除了APIView,还有两个常用的View:GenericAPIView 和 GenericViewSet 关系如下:

class GenericAPIView(APIView):
    pass # 10功能
class GenericViewSet(xxxx.View-2个功能, GenericAPIView):
    pass # 5功能能
class UserView(GenericViewSet): # 前面的功能都有了
    def get(self,request):
        pass

2. GenericAPIView

GenericAPIView 继承APIView,在APIView的基础上又增加了一些功能。例如:get_querysetget_object等。

实际在开发中一般不会直接继承它,他更多的是担任 中间人的角色,为子类提供公共功能。

from rest_framework.generics import GenericAPIView
class UserView(GenericAPIView):
    queryset = models.UserInfo.objects.filter(status=True)# 没加.all()的话会内部自动加上
    serializer_class = UserSerializer
    def get(self, request):
        # 获取数据库中的数据(其实就是上面写的类变量queryset)
        queryset = self.get_queryset()
        # 获取Serializer并实例化(其实就是上面写的类变量UserSerializer)
        ser = self.get_serializer(intance=queryset, many=True)
        print(ser.data)
        return Response({"code": 0, 'data': "..."})

可见GerericaAPIView内部定义了一些方法,我们将数据库查询、序列化类提取到类变量中,后期其它类再提供公共的get/post/put/delete等方法,让开发者只定义类变量,自动实现增删改查。

假如drf中有个XXXX类继承了GerericaAPIView实现了以下功能,那我们是不是以后不用写这种类似的代码,直接继承XXXX就可以了?

class XXXX(GenericAPIView):
    def get(self, request):
        queryset = self.get_queryset()
        ser = self.get_serializer(instance=queryset, many=True)
        return Response({'code': 0, "data": ser.data})

实际上确实是这样的:

当然我们也看到了还有GerericaAPIView里面paginator、filter_queryset方法,后面会讲。

总之实际在开发中一般不会直接继承它,他更多的是担任 中间人的角色,为子类提供公共功能。

3. GenericViewSet

平时我们写一些接口方法:

# urls.py
urlpatterns = [
    path('user/', views.UserView.as_view()),
    path('user/<int:pk>/', views.UserView.as_view()),
]
# views.py
class UserView(APIView):
    def get(self, request, pk=None):
        pass
    def post(self, request):
        pass
    def delete(self, request, pk):
        pass
    def put(self, request, pk):
        pass
    def patch(self, request, pk):
        pass
'''
接口:/users/      方法:GET     =>   用户列表
接口:/users/      方法:POST    =>   添加用户
接口:/users/(\d+)/  方法:GET     =>   获取单条数据
接口:/users/(\d+)/  方法:DELETE  =>   删除数据
接口:/users/(\d+)/  方法:PUT     =>   更新数据
接口:/users/(\d+)/  方法:PATCH   =>   局部更新
'''

而继承GenericViewSet后,可以这样写:

# urls.py
urlpatterns = [
    path('user/', views.UserView.as_view({
        'get': "list",
        'post': "create"
    })),
    path('user/<int:pk>/', views.UserView.as_view({
        'get': "retrieve",
        'put': "update",
        'patch': "partial_uptade",
        'delete': "destroy",
    })),
]
# views.py
class DepartModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Department
        fields = ['id', "title"]
        extra_kwargs = {
            "id": {"read_only": False},  # 数据验证 即post时必须传递  在get时显示
            "title": {"read_only": True}  # 序列化 即在get时显示 post时不用传递
        }
class RoleModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Role
        fields = ['id', "title"]
        extra_kwargs = {
            "id": {"read_only": False},  # 数据验证
            "title": {"read_only": True}  # 序列化
        }
class UserModelSerializer(serializers.ModelSerializer):
    level_text = serializers.CharField(source="get_level_display", read_only=True)
    # Serializer嵌套,不是read_only,一定要自定义create和update,自定义新增和更新的逻辑。
    depart = DepartModelSerializer(many=False)
    roles = RoleModelSerializer(many=True)
    extra = serializers.SerializerMethodField(read_only=True)
    email2 = serializers.EmailField(write_only=True)
    # 数据校验:username、email、email2、部门、角色信息
    class Meta:
        model = models.UserInfo
        fields = [
            "username", "age", "email", "level_text", "depart", "roles", "extra", "email2"
        ]
        extra_kwargs = {
            "age": {"read_only": True},
            "email": {"validators": [EmailValidator, ]},
        }
    def get_extra(self, obj):
        return 666
    def validate_username(self, value):
        return value
    # 新增加数据时
    def create(self, validated_data):
        """ 如果有嵌套的Serializer,在进行数据校验时,只有两种选择:
              1. 将嵌套的序列化设置成 read_only
              2. 自定义create和update方法,自定义新建和更新的逻辑
            注意:用户端提交数据的格式。
        """
        depart_id = validated_data.pop('depart')['id']
        role_id_list = [ele['id'] for ele in validated_data.pop('roles')]
        # 新增用户表
        validated_data['depart_id'] = depart_id
        user_object = models.UserInfo.objects.create(**validated_data)
        # 在用户表和角色表的关联表中添加对应关系
        user_object.roles.add(*role_id_list)
        return user_object
class UserView(GenericViewSet):
    # 认证、权限、限流等
    queryset = models.UserInfo.objects
    serializer_class = UserModelSerializer
    def list(self, request):
        # 业务逻辑:查看列表
        queryset = self.get_queryset()
        ser = self.get_serializer(instance=queryset, many=True)
        print(ser.data)
        return Response({"code": 0, 'data': ser.data})
    def create(self, request):
        # 业务逻辑:新建
        ser = self.get_serializer(data=request.data)
        if not ser.is_valid():
            return Response({'code': 1006, 'data': ser.errors})
        ser.validated_data.pop('email2')
        instance = ser.save(age=18, password="123", depart_id=1)
        print(instance)
        return Response({'code': 0, 'data': ser.data}) # ser.data返回新增的对象
    def retrieve(self, request, pk):
        # 业务逻辑:查看某个数据的详细
        user_obj = self.get_queryset().filter(id=pk).first()
        ser = self.get_serializer(instance=user_obj, many=False)
        return Response({"code": 0, 'data': ser.data})
    def update(self, request, pk):
        # 业务逻辑:全部修改
        return Response({'code': 0, 'data': "..."})
    def partial_update(self, request, pk):
        # 业务逻辑:局部修改
        return Response({'code': 0, 'data': "..."})
    def destory(self, request, pk):
        # 业务逻辑:删除
        return Response({'code': 0, 'data': "..."})

GenericViewSet类中没有定义任何代码,他就是继承 ViewSetMixin 和 GenericAPIView,也就说他的功能就是将继承的两个类的功能继承到一起。


GenericAPIView,将数据库查询、序列化类的定义提取到类变量中,便于后期处理。


ViewSetMixin,将 get/post/put/delete 等方法映射到 list、create、retrieve、update、partial_update、destroy方法中,让视图不再需要两个类。


注意:开发中一般也很少直接去继承他,因为他也属于是 中间人类,在原来 GenericAPIView 基础上又增加了一个映射而已。

3. 五大类

在drf的为我们提供好了5个用于做 增、删、改(含局部修改)、查列表、查单个数据的5个类(需结合 GenericViewSet 使用)。

# urls.py
from django.urls import path, re_path, include
from app01 import views
urlpatterns = [
    path('api/users/', views.UserView.as_view({"get":"list","post":"create"})),
    path('api/users/<int:pk>/', views.UserView.as_view({"get":"retrieve","put":"update","patch":"partial_update","delete":"destroy"})),
]
# views.py
from rest_framework.viewsets import GenericViewSet
from rest_framework.mixins import (
    ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin,
    DestroyModelMixin
)
class UserView(CreateModelMixin,RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin,ListModelMixin,GenericViewSet):
  # 认证、权限、限流等
    queryset = models.UserInfo.objects.filter(status=True)
    serializer_class = 序列化类

在这个5个类中已帮我们写好了 list、create、retrieve、update、partial_update、destory 方法,我们只需要在根据写 类变量:queryset、serializer_class即可。


对于获取多个数据,走ListModelMixin的list方法

       get: 127.0.0.1/api/user/  

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        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)
  • 对于获取单个数据,走RetrieveModelMixin中的retrieve方法

       get: 127.0.0.1/api/user/1/

    注:self.get_object()来自于 GenericAPIView内部定义,用于根据pk获取单个用户对象。没有则告知找不到。

class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)
  • 对于新增数据,走CreateModelMixin中的create方法

       post: 127.0.0.1/api/user/  

class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    def perform_create(self, serializer):
        serializer.save()
    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

注:

       如果此步骤中Serializer中新增了额外数据库中没有的字段,或者加了但不是read_only,由于自带的create中没有ser.pop("xxx"),在serializer.save()时会发生报错。可以选择自己重写create、perform_create。当然如果用不同的serializer,可以通过调用关系自己用自己的。

class UserView(CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, ListModelMixin,
               GenericViewSet):
    # 认证、权限、限流等
    queryset = models.UserInfo.objects.all()
    serializer_class = UserModelSerializer
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        from rest_framework import status
        serializer.validated_data.pop('email2')
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
       serializer.save(password='xxx')
       # 也可以重写GenericAPIView中的get_serializer_class区分用哪个校验器
       # 记得把read_only和write_only写清楚 其实公用一个校验器就行
   def get_serializer_class(self):
       if self.request.method == 'POST':
           return UserModelSerializer2
       return UserModelSerializer1
  • 对于删除某个数据,走DestoryModelMixin中的destory方法

       delete: 127.0.0.1/api/user/4/

class DestroyModelMixin:
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)
    def perform_destroy(self, instance):
        instance.delete()

对于更新某个数据,走UpdateModelMixin中的update/partial_update方法

       put:  127.0.0.1/api/user/1/

       patch:  127.0.0.1/api/user/1/

class UpdateModelMixin:
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)
        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}
        return Response(serializer.data)
    def perform_update(self, serializer):
        serializer.save()
    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

与上面post的create一样也有perform啥的,可以根据自己需求重写,这里不做演示。

注:


put:校验器中要求的字段(fileds中)都必须填写(extra_kwargs或类中新增字段加read_only除外,或default除外),不然报错。其返回内容也是校验器中的字段(不是传上去的字段,传的时候read_only可以不传,就算传了也改不了,但返回时你会看到)。

patch:可以只传一个或几个要改的键值对

update方法默认情况下不支持可写嵌套字段, 我们可以重写update,或在嵌套的序列化程序字段上设置好'read_only=True'   ,但如此一来之前的create就没法创建一对多多对多关系了。用一个校验器的话建议重写get_serializer_class改为用两个。

       depart = DepartModelSerializer(many=False, read_only=True)# 不建议

       roles = RoleModelSerializer(many=True, read_only=True)# 不建议

4. ModelViewSet

ModelViewSet是上面五大类的合并,当增删改查功能都出现时可以不用写那么多直接继承ModelViewSet。

from rest_framework.viewsets import ModelViewSet

当然如果只有一两个功能啥的还是建议单个继承。

5. 权限补充

在之前定义权限类时,类中可以定义两个方法:has_permission 和 has_object_permission


has_permission ,在请求进入视图之前就会执行。


has_object_permission,当视图中调用 self.get_object时就会被调用(删除、更新、查看某个对象时都会调用),一般用于检查对某个对象是否具有权限进行操作。

class PermissionA(BasePermission):
    message = {"code": 1003, 'data': "无权访问"}
    def has_permission(self, request, view):
        exists = request.user.roles.filter(title="员工").exists()
        if exists:
            return True
        return False
    def has_object_permission(self, request, view, obj):
        # 根据request.user是谁看有没有权限
        return True

我们在调用上面三个类的时候,会根据传过来的id/pk执行GenericAPIView中的get_object()在该方法中

它的内部便是调用了has_object_permission看看是否有权限进行操作。


区别:


has_permission ,在访问url进入视图之前就会执行。


has_object_permission,当视图中调用 self.get_object时就会被调用(删除、更新、查看某个对象时都会调用),一般用于检查对某个对象是否具有权限进行操作。


所以,让我们在编写视图类时,如果是直接获取间接继承了 GenericAPIView,同时内部调用 get_object方法,这样在权限中通过 has_object_permission 就可以进行权限的处理。


相关文章
|
2月前
|
安全 Java 数据库
SpingSecurity框架重要知识点(理解)
SpingSecurity框架重要知识点(理解)
38 1
|
8月前
|
JSON 前端开发 数据安全/隐私保护
DRF框架学习(四)
DRF框架学习(四)
|
8月前
|
JSON 前端开发 API
DRF框架学习(一)
DRF框架学习(一)
|
8月前
|
JSON 前端开发 数据库
DRF框架学习(三)
DRF框架学习(三)
|
8月前
|
JSON API 数据库
DRF框架学习(二)
DRF框架学习(二)
|
8月前
|
JSON 前端开发 Android开发
DRF框架使用时的一些注意点
DRF框架使用时的一些注意点
|
11月前
|
Python
高频面试题之Flask框架的上下文
Flask框架中分为2种上下文: 请求上下文(request context)和应用上下文(app context) 一般面试的问点在于:两种上下文是什么? 有什么区别? 什么场景下使用?
|
12月前
|
Java
接口(忽略的知识点)
接口属性 只能是final的,而且是public static final修饰符 比如int a=10; 其实上是public static final int a=10;
41 0
|
前端开发
前端项目实战224-axios封装中得工具类
前端项目实战224-axios封装中得工具类
76 0
|
JSON 算法 API