3

Django框架之drf:5、反序列化器校验部分源码分析、断言、drf之请求与响应、视图组件...

 1 year ago
source link: https://www.cnblogs.com/kangssssh/p/17090403.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Django框架之drf

一、反序列化类校验部分源码解析

反序列化校验什么时候开始执行校验

剖析流程一:在视图中使用反序列化器实例化的对象调用is_valid()的时候就会进行校验,通贩校验返回True,反之False

    class BookView(APIView):
		# 新增
		def post(self, request):
			ser_obj = BookSerializer(data=request.data)
          # 触发反序列化类校验
			if ser_obj.is_valid():
            ser_obj.save()
            return Response({'code': 100, 'msg': '新增图书成功', 'result': ser_obj.data})
          return Response({'code': 101, 'msg': ser_obj.errors})

解析流程二:入口:ser.is_valid() 是序列化类的对象,假设序列化类是BookSerializer---》is_valid---》找不到,在父类中找到BaseSerializer

def is_valid(self, *, raise_exception=False):
   if not hasattr(self, '_validated_data'):
     try:
        # self序列化类的对象,属性中没有_validated_data,一定会走这句【核心】
         self._validated_data = self.run_validation(self.initial_data)
          except ValidationError as exc:
                self._validated_data = {}
                self._errors = exc.detail
           else:
                self._errors = {}

       if self._errors and raise_exception:
           raise ValidationError(self.errors)

        return not bool(self._errors)

解析流程三:self._validated_data = self.run_validation(self.initial_data) 核心--》self序列化类的对象

  • 切记一定不要按住ctrl键点击

  • 真正的执行顺序是,从下往上找,找不到,再往上

  • 最终从Serializer类中找到了run_validation,而不是Field中的run_validation

def run_validation(self, data=empty):
    # 字段自己的,validates方法
    (is_empty_value, data) = self.validate_empty_values(data)
    if is_empty_value:
        return data
    # 局部钩子----【局部钩子】
    value = self.to_internal_value(data)
    try:

        self.run_validators(value)
        # 全局钩子--》如果在BookSerializer中写了validate,优先走它,非常简单
        value = self.validate(value)

    except (ValidationError, DjangoValidationError) as exc:
        raise ValidationError(detail=as_serializer_error(exc))

        return value
    
    
# 局部钩子  self.to_internal_value(data)---》self是BookSerializer的对象,从根上找
def to_internal_value(self, data):
    ret = OrderedDict()
    errors = OrderedDict()
    fields = self._writable_fields
    # fields写在序列化类中一个个字段类的对象
    for field in fields:
        # self BookSerializer的对象,反射validate_name
        validate_method = getattr(self, 'validate_' + field.field_name, None)
        try:
            # 在执行BookSerializer类中的validate_name方法,传入了要校验的数据
            validated_value = validate_method(validated_value)
        except ValidationError as exc:
            errors[field.field_name] = exc.detail

        else:
            set_value(ret, field.source_attrs, validated_value)

            if errors:
                raise ValidationError(errors)

                return ret

​ 用于判断一个表达式,在表达式的条件为False时会触发异常,反之正常往下执行代码,断言可以在条件不满足的时候主动抛出异常

关键词:assert

用法:

name = 'kangkang'

# 如果name不等于'kangkang',那么将会直接抛出异常代码
assert name== 'kangkang' 
print('正确,正常执行')
2987296-20230203204250265-868301578.png
2987296-20230203204256076-831445704.png

三、drf之请求

1、Request能够解析的前端传入编码格式

drf为我们提供了三种编码格式分别是:

  • 第一种:rest_framework.parsers.JSONParser

    • 对应前端的:json格式数据
  • 第二种:rest_framework.parsers.FormParser

    • 对应前端的:form-data格式数据
  • 第三种:rest_framework.parsers.MultiPartParser

    • 对应前端的:urlencode
2987296-20230203204302822-100404124.png
2987296-20230203204307350-328080691.png

配置请求编码的方法:

​ 这三种编码在默认都是开启的,配置的方法有两种:

  • 在视图类中配置(局部)

  • django的settings.py文件中配置(全局)

导入模块:(三种请求模块)
    from rest_framework.parsers import JSONParser, FormParser, MultiPartParser


# 方式一:在视图类中配置(只会影响该视图类下的方法)
    class BookView(APIView):
        parser_classes = ['JSONParser', 'FormParser', 'MultiPartParser']
     
    
# 方式二:django的settings.py中配置(影响全局)
	REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser',
    ],
    }
    
# 方式三:全局配了1个,某个视图类想要3个,怎么配?
    -只需要在视图类,配置3个即可
    -因为:先从视图类自身找,找不到,去项目的drf配置中找,再找不到,去drf默认的配置找

2、Request类中的属性和方法

# 该节在上述中已经讲过,此处只做总结
	继承APIView的视图类中的request是经过drf处理后的requset,该类不仅包含了django原生的request的所有方法,还新增了以下功能
    1、request.data  # (所有请求数据都可以在这里取出,升级版的request.body)
    2、__getattr__	# 该魔法方法是新的request中的方法,调用不存在的方法会回到老的requert中去找
    3、query_params  # 和老的request.get用法一致

四、drf之响应

1、Response能够响应的编码格式

​ drf是django的一个app,所以需要在django的settings.py中进行配置,否则浏览器将无法正常访问

​ drf的响应,使用浏览器和postman访问同一个接口,返回的格式是不一样的,如果是浏览器将会返回的格式好看一点,postman直接返回数据本身。响应的格式共有两种,配置方法类似于请求的配置方法

配置方法:

导入模块:(两种响应方式模块)
	from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
    
# 方式一:视图类中配置(局部配置,不会影响全局)
	class BookView(APIView):
    	renderer_classes=[JSONRenderer,]
        
# 方式二、django中配置项目文件(全局配置,局部配置的话优先采用局部)
    REST_FRAMEWORK = {
        'DEFAULT_RENDERER_CLASSES': [
            'rest_framework.renderers.JSONRenderer',
            'rest_framework.renderers.BrowsableAPIRenderer',
        ],
    }
    
# 方式三:使用顺序(一般就用内置的即可)
	优先使用视图类中的配置,其次使用项目配置文件中的配置,最后使用内置的

2、Response的源码属性或方法

Response源码分析

# 导入Response模块:
    from rest_framework.response import Response

# Response init可以传的参数
    def __init__(self, 
             data=None, 	
             status=None,
             template_name=None, 
             headers=None,
             exception=False, 
             content_type=None)
 
# data:
	之前咱们写的ser.data,可以是字典或列表,字符串---》序列化后返回给前端---》前端在响应体中看到的就是这个 
    
# status:
	http响应的状态码,默认是200,可以通过导入模块进行修改
        -from rest_framework.status import HTTP_200_OK
        -Response('xxx',status=status.HTTP_200_OK)
        
# template_name:
	解即可,修改响应模板的样子,BrowsableAPIRenderer定死的样子,后期公司可以自己定制
    
# headers:
	响应头,http响应的响应头
   		原生django,响应头中添加数据
        obj = HttpResponse('dddd')
            obj['xxc'] = 'yyc'
 		         return obj
            
# content_type :
	响应编码格式,一般不动

五、视图组件介绍及两个视图基类

APIView是drf提供的视图类中最顶层的视图基类

1、APIView与View区别

  • 传入到视图方法中的是REST framework的Request对象,而不是Django的HttpRequeset对象
  • 视图方法可以返回REST framework的Response对象
  • 任何APIException异常都会被捕获到,并且处理成合适的响应信息
  • 在进行dispatch()分发前,会对请求进行身份认证、权限检查、流量控制(执行三大认证)

2、视图基类

APIVIew
	-类属性:
    renderer_classes  # 响应格式
    parser_classes  # 能够解析的请求格式
    authentication_classes  #认证类
    throttle_classes  # 频率类
    permission_classes  # 权限类

六、基于APIView+ModelSerializer+Resposne写5个接口

1、视图类

from rest_framework.views import APIView
from rest_framework.response import Response
from .serializer import BookSerializer
from .models import Book


class BookView(APIView):
    # 查询多条
    def get(self, request):
        book_obj_all = Book.objects.all()
        serializer_obj = BookSerializer(instance=book_obj_all, many=True)
        return Response(serializer_obj.data)

    # 新增
    def post(self, request):
        res_obj = BookSerializer(data=request.data)
        res_obj.is_valid(raise_exception=True)
        res_obj.save()
        return Response({'cede': 100, 'msg': '新增成功', 'result': res_obj.data})


class BookDetailView(APIView):
    # 查询单条
    def get(self, request, *args, **kwargs):
        book_obj = Book.objects.filter(**kwargs).first()
        serializer_obj = BookSerializer(instance=book_obj)
        return Response(serializer_obj.data)

    # 修改
    def put(self, request, *args, **kwargs):
        target_book_obj = Book.objects.filter(**kwargs).first()
        serializer_obj = BookSerializer(instance=target_book_obj, data=request.data)
        serializer_obj.is_valid(raise_exception=True)
        serializer_obj.save()
        return Response({'code': 100, 'msg': '修改成功', 'result': serializer_obj.data})

    # 删除
    def delete(self, request, *args, **kwargs):
        Book.objects.filter(**kwargs).delete()
        return Response({'code': 100, 'msg': '删除成功'})

2、序列化类

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Book

# 序列化类
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        # 绑定的表
        model = Book
        # 序列化的字段
        fields = ['pk', 'name', 'price', 'authorList', 'publishDetail', 'author', 'publish']
        
        # 字段校验条件
        extra_kwargs = {
            'name': {'max_length': 32},
            'authorList': {'read_only': True},
            'publishDetail': {'read_only': True},
            'author': {'write_only': True},
            'publish': {'write_only': True}
        }

    # 全局钩子
    def validate(self, attrs):
        if int(attrs.get('price')) > 100:
            raise ValidationError('价格不能超过100元')
        return attrs

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 查询多条、新增接口
    path('book/', views.BookView.as_view()),
    # 查询单条、修改、删除接口
    path('book/<int:pk>/', views.BookDetailView.as_view())
]

4、模型类

from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    author = models.ManyToManyField(to='Author')
    
    # 外键字段数据序列化功能函数
    def authorList(self):
        author_data_list = [{'name': author_obj.name,
                             'phone': author_obj.phone,
                             'age': author_obj.authorinfo.age,
                             'address': author_obj.authorinfo.gender
                             } for author_obj in self.author.all()]
        return author_data_list

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)

    # 外键字段数据序列化功能函数
    def publishDetail(self):
        return {'name': self.publish.name, 'address': self.publish.address}


class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(max_length=11)

    authorinfo = models.OneToOneField(to='AuthorInfo', on_delete=models.CASCADE, null=True)


class AuthorInfo(models.Model):
    age = models.CharField(max_length=8)
    gender = models.CharField(max_length=8)


class Publish(models.Model):
    name = models.CharField(max_length=32)
    address = models.CharField(max_length=32)

七、基于GenericAPIView+5个视图扩展类

1、视图类

from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin, CreateModelMixin, UpdateModelMixin, DestroyModelMixin, \
    RetrieveModelMixin
from .serializer import BookSerializer
from .models import Book


class BookView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    # 查询多条
    def get(self, request):
        return self.list(request)

    # 新增
    def post(self, request):
        return self.create(request)


class BookDetailView(GenericAPIView,
                     RetrieveModelMixin,
                     DestroyModelMixin,
                     UpdateModelMixin):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    # 查询单条
    def get(self, request, *args, **kwargs):
        return self.retrieve(request, *args, **kwargs)

    # 修改
    def put(self, request, *args, **kwargs):
        return self.update(request, *args, **kwargs)

    # 删除
    def delete(self, request, *args, **kwargs):
        return self.destroy(request, *args, **kwargs)

2、序列化类

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import Book

# 序列化类
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        # 绑定的表
        model = Book
        # 序列化的字段
        fields = ['pk', 'name', 'price', 'authorList', 'publishDetail', 'author', 'publish']

        # 字段校验条件
        extra_kwargs = {
            'name': {'max_length': 32},
            'authorList': {'read_only': True},
            'publishDetail': {'read_only': True},
            'author': {'write_only': True},
            'publish': {'write_only': True}
        }

    # 全局钩子
    def validate(self, attrs):
        if int(attrs.get('price')) > 100:
            raise ValidationError('价格不能超过100元')
        return attrs
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    # 查询多条、新增接口
    path('book/', views.BookView.as_view()),
    # 查询单条、修改、删除接口
    path('book/<int:pk>/', views.BookDetailView.as_view())
]

4、模型类

from django.db import models


class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    author = models.ManyToManyField(to='Author')

    # 外键字段数据序列化功能函数
    def authorList(self):
        author_data_list = [{'name': author_obj.name,
                             'phone': author_obj.phone,
                             'age': author_obj.authorinfo.age,
                             'address': author_obj.authorinfo.gender
                             } for author_obj in self.author.all()]
        return author_data_list

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE, null=True)

    # 外键字段数据序列化功能函数
    def publishDetail(self):
        return {'name': self.publish.name, 'address': self.publish.address}


class Author(models.Model):
    name = models.CharField(max_length=32)
    phone = models.CharField(max_length=11)

    authorinfo = models.OneToOneField(to='AuthorInfo', on_delete=models.CASCADE, null=True)


class AuthorInfo(models.Model):
    age = models.CharField(max_length=8)
    gender = models.CharField(max_length=8)


class Publish(models.Model):
    name = models.CharField(max_length=32)
    address = models.CharField(max_length=32)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK