DRF序列化组件

文章目录


一.CBV源码分析和APIView源码分析

CBV源码分析

1.代码分析

切入点:url.py中的.as_view()方法

path('test/',views.TestView.as_view()),

2.CBV的执行流程

  1. path的第二个参数是:View类的as_view内部有个view闭包函数内存地址

  2. 一旦有请求来了,匹配test路径成功

  3. 执行第二个参数view函数内存地址(requset)

  4. 本质执行了self.dispatch(request)

  5. 通过反射去获得方法(如果是get请求,就是get方法)

    if request.method.lower() in self.http_method_names:
    	handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    
  6. 执行get方法,传入参数

    handler(request, *args, **kwargs)
    

APIView源码分析

1.代码分析

与CBV的源码分析过程一样,我们也是从路由中的as_view()作为切入点

path('test/',APIView类的as_view内部是用了View的as_view内的view闭包函数),

2.API的执行流程

  1. path的第二个参数是:APIView类的as_view内的view闭包函数

  2. 一旦请求来了,匹配test路径成功

  3. 执行第二个参数view函数内存地址(requset),其实是执行Viewas_view内的view闭包函数,但是加了个csrf_exempt装饰器

  4. 所以,继承了APIView的所有接口,都没有csrf的校验了(************************************)

  5. 执行self.dispatch(request)----》APIView类的

    def dispatch(self, request, *args, **kwargs):
                # 以后所有的request对象,都是****新的request对象***,它是drf的Request类的对象
                request = self.initialize_request(request, *args, **kwargs)
                self.request = request
                try:
                    #整个drf的执行流程内的权限,频率,认证
                    self.initial(request, *args, **kwargs)
                    if request.method.lower() in self.http_method_names:
                        handler = getattr(self, request.method.lower(),
                                          self.http_method_not_allowed)
                    else:
                        handler = self.http_method_not_allowed
        
                    response = handler(request, *args, **kwargs)
        
                except Exception as exc:
                    # 全局异常
                    response = self.handle_exception(exc)
                # 响应
                self.response = self.finalize_response(request, response, *args, **kwargs)
                return self.response
    
        '''
        
    ### request = self.initialize_request(request, *args, **kwargs)
    ##返回的request对象是drf   Request类的request对象
    def initialize_request(self, request, *args, **kwargs):
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    ### *******以后,在视图类中使用的request对象已经不是原来的request对象了,现在都是drf的request对象了
    

3.注意点:(重点)

  1. 所有的csrf都不校验了
  2. request对象变成了新的request对象,drfrequest对象
  3. 执行了权限,频率,认证
  4. 捕获了全局异常(统一处理异常)
  5. 处理了response对象,如果浏览器访问是一个样,postman访问又一个样
  6. 以后,在视图类中使用的request对象已经不是原来的request对象了,现在都是drf的request对象了

二、Request对象分析

通过以上对APIView的分析

  • django原生的request对象:django.core.handlers.wsgi.WSGIRequest
  • drfrequest对象:rest_framework.request.Request
  • drf 的 request 对象中有原生的 request 对象 : self._request

图解:

DRF序列化组件_DRF

DRF序列化组件_Django_02

DRF序列化组件_DRF_03

疑问

  • 在视图类中使用 request 对象, 从中取出想要的数据
# 正常取值应该是使用 "_request"
request._request.method

# 但我们使用的却是
request.method

DRF序列化组件_DRF_04

如何实现这种操作?

  • 对象.属性会触发 类的__getattr__方法
验证:
  • drf的Request类重写了__getattr__
def __getattr__(self, attr):
    try:
        # 去原生的request反射属性
        return getattr(self._request, attr)
    except AttributeError:
        return self.__getattribute__(attr)

在 Response 类中找到 __getattr__, 当属性在 drf 中的 request 中找不到时, 就会通过反射的方式从原生的 request 对象中取值

DRF序列化组件_DRF_05

虽然视图类中request对象变成了drf的request,但是用起来,跟原来的一样,只不过它多了一些属性

  • request.datapost请求提交的数据,不论什么格式,都在它中
  • requst.query_paramsget请求提交的数据(查询参数)

重点记住:

drf的request对象用起来跟原来一样(重写了__getattr__)
request.data  #post请求提交的数据,不论什么格式,都在它中
requst.query_params# get请求提交的数据(查询参数)

三、序列化器介绍 (Serializer)

1.Python中的序列化与反序列化

  • 序列化:把python中的对象转成json格式字符串
  • 反序列化:把json格式字符串转成python中的对象

2.序列化器的作用

  • 序列化, 序列化器会把模型对象转换成字典, 经过 response 以后变成json字符串
  • 反序列化, 把客户端发送过来的数据,经过 request 以后变成字典, 序列化器可以把字典转成模型

  • 反序列化, 完成数据校验功能

3.注意:

  • drf 中的序列化组件是先把对象转成字典格式, 因为字典格式可以直接放进 Response

4.使用序列化器的执行流程

DRF序列化组件_DRF_06

四、序列化器的简单使用

使用步骤

  1. 写一个序列化的类,并继承Serializer
  2. 在类中写要序列化的字段, 序列化字段类(有很多, 常用的就几个, 等同于models中的字段类)
  3. 在视图类中使用, 导入序列化类把要序列化的对象传入, 得到序列化对象
  4. 得到序列化的数据,通过[序列化类的对象].data获取序列化后的字典

ps:: 如果不使用rest_framework提供的Response,就得使用JsonResponse

代码示例:

  • models.py 创建模型类
from django.db import models
class Book2(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    price = models.IntegerField(max_length=8)
    author = models.CharField(max_length=16)
    publish = models.CharField(max_length=16)
  • serializers.py 创建序列化器
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from drf_test import models

# 定义一个序列化器,继承ModelSerializer
class BookSerializer2(serializers.Serializer):
    # 里面书写想要序列化的字段,想序列哪个就书写哪个
    title = serializers.CharField()
    price = serializers.IntegerField()
  • views.py书写视图类
from rest_framework.viewsets import ModelViewSet
from drf_test.serializers import BookSerializer2
from drf_test import models

class Book2View(APIView):
    def get(self,request,id):
        # 从数据库中获取到Book的对象
        book_obj = models.Book2.objects.filter(nid=id).first()
        # 将book_obj放进我们写的序列化器里面进行序列化
        book_ser = BookSerializer2(book_obj)
        # [序列化对象].data就是序列化后得到的字典
        return Response(book_ser.data)
  • urls.py 文件
re_path('^books/(?P<id>\d+)', views.Book2View.as_view()),
  • 开始项目测试

DRF序列化组件_DRF_07

DRF序列化组件_DRF_08

五、序列化类的字段类型和参数

1.常用字段类型

字段 字段构造方式
BooleanField BooleanField()
NullBooleanField NullBooleanField()
CharField CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
EmailField EmailField(max_length=None, min_length=None, allow_blank=False)
RegexField RegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugField SlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9*-]+
URLField URLField(max_length=200, min_length=None, allow_blank=False)
UUIDField UUIDField(format=‘hex_verbose’) format: 1) 'hex_verbose'"5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 2) 'hex'"5ce0e9a55ffa654bcee01238041fb31a" 3)'int' - 如: "123456789012312313134124512351145145114" 4)'urn' 如: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
IPAddressField IPAddressField(protocol=‘both’, unpack_ipv4=False, **options)
IntegerField IntegerField(max_value=None, min_value=None)
FloatField FloatField(max_value=None, min_value=None)
DecimalField DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
DateTimeField DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
DateField DateField(format=api_settings.DATE_FORMAT, input_formats=None)
TimeField TimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DurationField DurationField()
ChoiceField ChoiceField(choices) choices与Django的用法相同
MultipleChoiceField MultipleChoiceField(choices)
FileField FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageField ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListField ListField(child=, min_length=None, max_length=None)
DictField DictField(child=)

2.字段参数

  • 针对 CharField 类型
参数 作用
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
  • 针对 InterField 类型
参数 作用
max_value 最小值
min_value 最大值
  • 通用参数(所有字段都有的)
参数 作用
read_only(最重要) 表明该字段仅用于序列化输出,默认False
write_only(最重要) 表明该字段仅用于反序列化输入,默认False
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息

六、序列化器的数据校验及数据保存

1.使用步骤

  1. 写一个序列化的类,继承Serializer;如果要反序列化,继承了Serializer,必须重写create方法。

    def create(self, validated_data):
        res = models.Publish.objects.create(**validated_data)
        return res
    
  2. 在类中写要反序列化的字段,想反序列化哪个字段,就在类中写哪个字段,字段的属性

    max_length	最大长度
    min_lenght	最小长度
    allow_blank	是否允许为空
    trim_whitespace	是否截断空白字符
    max_value	最小值
    min_value	最大值
    
  3. 在视图类中使用,导入自定义的继承继承Serializer类的序列化类,把要要修改的对象传入,得到序列序列化对象

  4. 数据校验if publish_ser.is_valid():

  5. 如果校验通过,就保存

  6. 如果不通过,返回restful规范的返回结果

  7. 如果字段的校验规则不够,可以写钩子函数(局部和全局)

    def post(self, request):
        publish_ser = serializer.PublishSerializer(data=request.data)
        if publish_ser.is_valid():
            # 直接保存,保存到哪个表里?需要重写save
            publish_ser.save()
            return Response(publish_ser.data)
        else:
            print(publish_ser.errors)
            return Response('数据有问题啊')
    

2.数据的校验

  • 与forms 组件的数据校验方式类似, 都是调用 is_valid() 方法进行校验
  • 先对字段自带校验规则进行校验,然后局部钩子,最后全局钩子
  • 使用序列化器进行反序列化时,需要对数据进行验证后, 才能获取验证成功的数据或保存成模型类
# 在视图类中书写
class PublishView(APIView):
    # 新增数据方法
    def post(self,request):
        # 传入前端提交的json格式数据,拿到反序列化的对象
        publish_ser = serializer.PublishSerializer(data=request.data)
        # 使用 is_valid() 方法进行数据校验,返回 True 和 False
        if publish_ser.is_valid():
            # 手动保存
            # 拿到创建的对象
            res = modeels.Publish.objects.create(**publish_ser.validated_data)
            # 将其赋值给instance
            publish_ser.instance = res
            return Response(publish_ser.data)
        else:
            return Response(publish_ser.errors)

3.数据的保存

  • 上面我们通过手动将新增对象赋值给序列器对象的instance来进行保存
  • 其实我们可以直接使用序列器对象的.save()方法来实现保存
  • 但是使用.save()方法明确规定必须重写该方法

如果继承了Serializer, 但凡是数据的新增还是更新, 调用 .save() 方法进行数据保存的时候都需要重写 create方法和 update方法, 如果继承的是ModelsSerializer则不需要重写

  • serializer.py 序列化器内重写方法
from rest_framework import serializers

class PublishSerializer(serializers.Serializer):
    ...
    
    # 重写create方法
    def create(self,valodated_data):
        res = models.Punlish.objects.create(**validated_data)
        return res
        
    # 重写update方法
    def update(self,instance,validated_data):
        # 手动获取数据进行赋值修改
        name = validated_data.get('name')
        city = validated_date.get('city')
        email = validated_data.get('email')
        instance.name = name
        instance.city = city
        instance.email = email
        instance.save()
        return instance
  • views.py 视图类中使用 .save()
class PublishView(APIView):
    ...
    
    # 新增数据
    def post(self,request):
        publish_ser = serializer.PublishSerializer(data=request.data)
        if publish_ser.is_valid():
            # 直接调用 .save() 保存
            publish_ser.save()
            return Response(publish_ser.data)
        else:
            return Response(publish_ser.errors)
        
class PublishDetailView(APIView):
    ...
    
    # 修改数据
    def put(self,request,id):
        publish = models.Publish.objects.filter(pk=id).first()
        # 传入前端要修改的对象及传过来的数据 
        publish_ser = serializer.PublishSerializer(instance=publish,data=request.data)
        # 得到反序列化的对象进行校验
        if publish_ser.is_valid():
            publish_ser.save()
            return Response(publish_ser.data)
        else:
            return Response(publish_ser.errors)

输出的错误信息(errors)是英文, 要想让错误显示的是中文自定义的话, 方法与 forms 组件的方法是一样的 : 再模型类字段中添加 error_massages 参数来指定

4.数据保存之重写save方法

  • 还有一种就是重写 save 方法, 但官方不推荐重写该方法 (作为了解)
  • 序列化类中重写save
class PublishSerializer(serializers.Serializer):
    ...
	
	# 重写save
    def save(self, **kwargs):  # kwargs是校验过后的数据
        # 在父类的save内部, 调用了create, 所以我们重写create,将数据传入
        res=models.Publish.objects.create(**kwargs)
        return res
  • 视图类中的使用
class PublishView(APIView):
    ...
    
    # 新增数据
    def post(self,request):
        publish_ser = serializer.PublishSerializer(data=request.data)
        if publish_ser.is_valid():
            # 重写save需要手动传参
            res = publish_ser.save(**publish_ser.validated_data)
            publish_ser.instance = res
            return Response(publish_ser.data)
        else:
            return Response(publish_ser.errors)

5.is_valid( ) 的 raise_exception 参数

  • is_valid() 方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception = True参数开启,REST框架接收到此异常,会向前端返回HTTP 400错误的请求响应
publish_ser.is_valid(raise_exception=True)

七、序列化器三种字段校验方法

1.序列化子内字段自带的校验

# 在书写序列化类字段时加入参数来控制校验条件
class PublishSerializer(serializers.Serializer):
    nid = serializers.IntegerField(required=False)  # 必填
    name = serializers.CharField(min_length=3)     # 最短长度为3
    city = serializers.CharField(max_length=16)    # 最长为16
    email = serializers.EmailField()

2.使用钩子函数(全局钩子,局部钩子)

  • 局部钩子 : validate_[字段名]
# 校验名字不能以"xxx"开头
def validate_name(self, data):
    # data就是当前字段的值
    if data.startswith('xxx'):
   	    raise ValidationError('不能以"xxx"开头')
    else:
        return data  # 返回校验后的值
  • 全局钩子:validate
# 校验名字不能与所在城市同名
def validate(self, attrs): 
    name = attrs.get('name')
    city = attrs.get('city')
    if name == city:
        raise ValidationError('名字不能和城市名一样!')
    else:
        return attrs  # 返回所有数据

3.使用字段参数 validators 来进行校验

  • 该参数可以指定校验函数, 适用于多个字段都要进行同一种校验
  • 如果每个都写钩子就很麻烦, 于是就可以写一个校验函数, 每个字段都使用 validators 参数来指定该函数
# 定义一个校验函数
def name_start(data):
    if data.startswith('xxx'):
        raise ValidationError('不能以"xxx"开头')
    else:
        return data
        
# 然后在需要进行该校验的字段中加入 validators 参数指定该校验函数
author = serializers.CharField(validators=[name_start])
...
city = serializers.CharField(validators=[name_start])
...

八、read_only 和 write_only

1.常用字段参数

  • read_only:表明该字段仅用于序列化输出,默认False(只能查看,不能修改)
  • write_only:表明该字段仅用于反序列化输入,默认False(只能修改,不能查看)

2.示例代码

  • urls.py
re_path('^books/(?P<id>\d+)', views.Book2View.as_view()),
  • models.py
class Book2(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=16)
    price = models.IntegerField()
    author = models.CharField(max_length=16, null=True)
    publish = models.CharField(max_length=16, null=True)
  • serializers.py
from rest_framework import serializers


class BookSerializer2(serializers.Serializer):
    name = serializers.CharField(max_length=16)
    price = serializers.IntegerField()
    # read_only=True:该字段只用于序列化输出,只能看不能修改
    author = serializers.CharField(max_length=16, read_only=True)
    # write_only=True:该字段只用于反序列化输入,只能改不能查看
    publish = serializers.CharField(max_length=19, write_only=True)

    def update(self, instance, validated_data):
        instance.title = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.author = validated_data.get('author')
        instance.publish = validated_data.get('publish')
        instance.save()
        return instance
  • views.py
from mydrf.serializers import BookSerializer2
from mydrf import models
from rest_framework.views import APIView
from rest_framework.response import Response


class Book2View(APIView):
    def get(self, request, id):
        book_obj = models.Book2.objects.filter(nid=id).first()
        book_ser = BookSerializer2(book_obj)
        return Response(book_ser.data)

    def put(self, request, id):
        book_obj = models.Book2.objects.filter(nid=id).first()
        book_ser = BookSerializer2(instance=book_obj, data=request.data)
        if book_ser.is_valid():
            book_ser.save()
            return Response(book_ser.data)
        else:
            return Response(book_ser.errors)
  • 查询

DRF序列化组件_Django_09

  • 修改

DRF序列化组件_DRF_10

DRF序列化组件_DRF_11

DRF序列化组件_DRF_12

九、实现增删改查接口

1.需求

  • 数据库表:Book,Author,AuthorDatail, Publish,表之间建好关系
  • 实现查全部, 查某条, 新增, 修改, 删除接口
  • 出版社名字不能有"xxx"敏感字

2.代码实现

  • models.py
from django.db import models


class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField()

    publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def __str__(self):
        return self.name


class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_datail = models.OneToOneField(to='AuthorDatail', to_field='nid', unique=True, on_delete=models.CASCADE)


class AuthorDatail(models.Model):
    nid = models.AutoField(primary_key=True)
    telephone = models.BigIntegerField()
    birthday = models.DateField()
    addr = models.CharField(max_length=64)


class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32, null=True)
    email = models.EmailField()

    def __str__(self):
        return self.name

  • serializers.py
from app01 import models
from rest_framework import serializers
from rest_framework.exceptions import ValidationError


class PublishSerializer(serializers.Serializer):
    nid = serializers.IntegerField(required=False)
    name = serializers.CharField(max_length=16)
    city = serializers.CharField(max_length=32,error_messages={"max_length": "太长"})
    email = serializers.EmailField()

    # 重写create
    def create(self, validated_data):
        res = models.Publish.objects.create(**validated_data)
        return res

    # 重写update
    def update(self, instance, validated_data):
        instance.name = validated_data.get('name')
        instance.city = validated_data.get('city')
        instance.email = validated_data.get('email')
        instance.save()
        return instance

    # 局部钩子,校验出版社名字
    def validate_name(self, data):
        if "小黄书" in data:
            raise ValidationError(f'存在敏感字符{data}')
        else:
            return data

    # 全局钩子
    def validate(self, attrs):
        name = attrs.get('name')
        city = attrs.get('city')
        if name == '小黑出版社' and city == "上海":
            raise ValidationError('该出版社已被列入黑名单')
        return attrs
  • views.py
from app01.serializers import PublishSerializer
from app01 import models
from rest_framework.views import APIView
from rest_framework.response import Response


class PublishView(APIView):
    # 获取所有书
    def get(self, request):
        publish_qs = models.Publish.objects.all()
        publish_ser = PublishSerializer(instance=publish_qs, many=True)  # 有多条数据的时候一定要添加
        return Response(publish_ser.data)

    # 新增数据
    def post(self, request):
        publish_ser = PublishSerializer(data=request.data)
        if publish_ser.is_valid():
            publish_ser.save()
            return Response(publish_ser.data)
        else:
            # publish_ser.errors
            return Response("error")


class PublishView2(APIView):
    # 获取一条
    def get(self, request, id):
        publish_obj = models.Publish.objects.filter(pk=id).first()
        publish_ser = PublishSerializer(instance=publish_obj)
        return Response(publish_ser.data)

    # 修改数据
    def put(self, request, id):
        publish_obj = models.Publish.objects.filter(pk=id).first()
        publish_ser = PublishSerializer(instance=publish_obj, data=request.data)
        if publish_ser.is_valid():
            publish_ser.save()
            return Response(publish_ser.data)
        else:
            return Response(publish_ser.errors)

    # 删除数据
    def delete(self, request, id):
        # 直接删除数据,拿到影响条数的一个列表
        rows = models.Publish.objects.filter(pk=id).delete()
        # 取出来判断一下
        if rows[0] > 0:
            return Response('数据删除成功!')
        else:
            return Response('数据不存在!')
  • urls.py
path('publish/', views.PublishView.as_view()),
path('publish/<int:id>', views.PublishView2.as_view()),

3.测试效果

DRF序列化组件_Django_13

  • 新增出版社

DRF序列化组件_Django_14

DRF序列化组件_Django_15

  • 带敏感词创建不了

DRF序列化组件_Django_16

DRF序列化组件_DRF_17

  • 黑名单创建不了

DRF序列化组件_Django_18

DRF序列化组件_Django_19

  • 查某一条

DRF序列化组件_Django_20

  • 修改数据

DRF序列化组件_Django_21

DRF序列化组件_Django_22

  • 删除数据

DRF序列化组件_Django_23

DRF序列化组件_DRF_24

  • 删除不存在的数据

DRF序列化组件_Django_25

十、source参数

用来指定序列化的字段(数据表中的字段),一般使用在一个字段情况,可以跨表,可以执行方法并执行

  • 不指定,默认就是字段名,必须跟数据库对应
  • 指定了source就可以给字段改名了
# 更改字段名
"[另取的名字]" = serializers.CharField(source='publish')  # 指定需要序列化的字段是publish

# 跨表
publish = serializers.CharField(source='publish.city')

# 执行方法,指定一个定义在模型类内方法的内存地址,会自动加括号执行
pub_date = serializers.CharField(source='times')

十一、SerializerMethodField( )

一般用于跨表查询, 在序列化器内部写一个get_[外键名],会显示指定的数据(就是显示多张表的结果)

1.需求

  • 获取书籍信息, 并获取到该数据下的出版社信息以及所有的作者信息

2.方式一:使用 SerializerMethodField 实现

  • Serializer.py
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()

    # 取出该书的出版社
    publish = serializers.SerializerMethodField()
    def get_publish(self, obj):
        return {'name': obj.publish.name, 'city': obj.publish.city, 'email': obj.publish.email}

    # 取出该书的所有作者
    authors = serializers.SerializerMethodField()
    ## for循环出所有作者
    # def get_authors(self, obj):
    #     li = []
    #     for author in obj.authors.all():
    #         li.append({'id':author.nid,'name':author.name,'age':author.age})
    #         return li

    # 列表生成式书写
    def get_authors(self, obj):
        return [{'id': author.nid, 'name': author.name, 'age': author.age} for author in obj.authors.all()]
  • views.py
from mydrf.serializers import BookSerializer

class BookView(APIView):
    def get(self, request):
        book_qs = models.Book.objects.all()
        book_ser = BookSerializer(instance=book_qs, many=True)
        return Response(book_ser.data)
  • urls.py
path('books/', views.BookView.as_view()),
  • 测试效果

DRF序列化组件_DRF_26

3.方式二:在模型类中书写方法

该方法可以自定义输出的内容, 比如对一些数据进行处理后返回前端(身份证中间部分加"*****")

  • models.py
class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField()

    publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def __str__(self):
        return self.name

    # 取出该书的出版社
    def get_publish(self):
        return {'name': self.publish.name, 'city': self.publish.city, 'email': self.publish.email}

    # 取出该数的所有作者
    def get_author(self):
        return [{'id': author.nid, 'name': author.name, 'age': author.age} for author in self.authors.all()]

  • serializers.py
class BookSerializer(serializers.Serializer):
    name = serializers.CharField()
    price = serializers.IntegerField()

    # 获取出版社和作者
    publish = serializers.DictField(source="get_publish")
    authors = serializers.ListField(source="get_author")
  • 测试效果

DRF序列化组件_DRF_27

十二、模型类序列化器

1.Serializer与ModelSerializer

  • 上面我们使用的 Serializer 序列化类它不是只能为数据库模型类定义, 也可以为非数据库模型类的数据定义 (也就是自己定义的数据进行序列化 : {“name”:“shawn”,“age”:18}),

  • serializer是独立于数据库之外的存在, 需要自己在序列化类中去指定模型类的字段

  • 而如果我们想要使用序列化器对应的是Django的模型类, DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类

2.ModelSerializer相比较于Serializer提供了一些功能

  • 基于模型类自动生成一系列字段 (也就是不再需要自己手动定义字段)
  • 基于模型类自动为 Serializer 生成 validators,比如 : unique_together
  • 包含默认的create( )update( )的实现, 不再需要重写这两种方法

3.模型类序列化器的定义

  • model:指明参照哪个模型类
  • fields:指明为模型类的哪些字段生成
class AuthorDatailSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.AuthorDatail
        fields = '__all__'
  • exclude : 可以排除某这些字段, 不能与 fields 同时存在
class AuthorDatailSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.AuthorDatail
        exclude = ('phone',)
  • depth : 显示的深度一本书有作者, 作者有作者信息, 定义该参数会一层层显示, 层数自己控制(了解)
  • read_only_field : 指明只读字段, 仅用于序列化输出的字段 (弃用,有更好的方法)
  • wirte_only_field : 指明只写字段, 仅用于反序列化输入的字段 (弃用)
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        exclude = ('age',)
        depth = 3
        read_only_fields = ('nid', 'price')
        ...
  • extra_kwargs : 使用该参数为ModelSerializer添加或修改原有的选项参数 (重点, 更好的方法)
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
        extra_kwargs = {
            'price': {'min_value': 0, 'required': True},
            'publish': {'required': True, 'read_only_fields' : True},
        }

4.示例:使用一个接口来同时新增 Author 和 AuthorDetail 表

模型表还是上面创建的模型表, 只是新增了点东西

  • models.py
class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_datail = models.OneToOneField(to='AuthorDatail', to_field='nid', unique=True, on_delete=models.CASCADE)
		
    # 获取用户的详情    
    def get_detail(self):
        return {"telephone":self.author_datail.telephone,"addr":self.author_datail.addr,"birthday":self.author_datail.birthday}

  • serializers.py
class AuthorSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Author
        fields = ['nid', 'name', 'age', 'detail', 'author_datail']

    # 获取用户信息数据
    detail = serializers.DictField(source="get_detail", read_only=True)


class AuthorDatailSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.AuthorDatail
        fields = '__all__'
  • views.py
from mydrf.serializers import AuthorSerializer
from mydrf.serializers import AuthorDatailSerializer


class AuthorView(APIView):
    def get(self, request):
        author_qs = models.Author.objects.all()
        author_ser = AuthorSerializer(instance=author_qs, many=True)
        return Response(author_ser.data)

    def post(self, request):
        # 将用户详情数据取出来
        author_detail = request.data.pop('detail')
        # 将剩余的用户数据赋值
        author_dict = request.data
        # 先将用户详情进行序列化操作并保存
        author_detail_ser = AuthorDatailSerializer(data=author_detail)
        if author_detail_ser.is_valid():
            author_detail_ser.save()
            # 取出用户详情数据的"nid',并将其添加到用户表的"author_datail"字段
            author_detail_nid = author_detail_ser.data.get("nid")
            author_dict["author_datail"] = author_detail_nid
            # 再将用户信息数据进行序列化并保存
            author_ser = AuthorSerializer(data=author_dict)
            if author_ser.is_valid():
                author_ser.save()
                return Response(author_ser.data)
            else:
                return Response(author_ser.errors)
        else:
            return Response(author_detail_ser.errors)
  • urls.py
path('author/', views.AuthorView.as_view()),
  • 效果演示

DRF序列化组件_DRF_28

两张表都新增了数据

  • Author 表

DRF序列化组件_Django_29

  • AuthorDeail 表

DRF序列化组件_Django_30

十三、序列化类源码分析(了解)

1.many参数

  • 先调用__new__方法
  • 如果 many=True, 生成ListSerializer对象
  • 如果为 many=False,生成Serializer对象
1 序列化类实例化的时候,传了many,序列化多条,不传,就序列化单条
#many=True,实例化得到的对象是ListSerializer
ser=serializer.BookModelSerializer(instance=qs,many=True)
print(type(ser))	#rest_framework.serializers.ListSerializer

# 列表中套了很多BookModelSerializer

#many=False,实例化得到的对象是BookModelSerializer
ser=serializer.BookModelSerializer(instance=book)
print(type(ser))	#app01.serializer.BookModelSerializer


类实例化:在执行__init__之前,先执行了__new__生成一个空对象(决定了是哪个类的对象)
在__new__中进行判断,如果many=True,就返回ListSerializer的对象

列表中套了很多BookModelSerializer

DRF序列化组件_Django_31

DRF序列化组件_Django_32

DRF序列化组件_Django_33

2.局部全局钩子的源码分析

入口:
is_valid()
执行流程分析:
  • 首先判断_validated_data

  • 如果没有,执行了 self.run_validation(self.initial_data)

    注意:目前在BaseSerializer,如果按住ctrl点击,会直接进到它父类的run_validation,进到Field,不是真正执行的方法

    DRF序列化组件_Django_34

  • 我们需要从头找,实际上是Serializer类的run_validation

def run_validation(self, data=empty):
    value = self.to_internal_value(data)#字段自己的校验和局部钩子
    try:
        self.run_validators(value) 
        value = self.validate(value) # 全局钩子
        assert value is not None, 
        except (ValidationError, DjangoValidationError) as exc:
            raise ValidationError(detail=as_serializer_error(exc))

            return value
局部钩子是在  to_internal_value执行的
def to_internal_value(self, data):
    for field in fields:
    	validate_method = getattr(self, 'validate_' + field.field_name, None)
        if validate_method is not None:
            validated_value = validate_method(validated_value)
图解:

DRF序列化组件_Django_35

从头开始找,在自己写的serializer里面找到Serializer

DRF序列化组件_DRF_36

局部钩子:

  • 局部钩子是在to_internal_value执行的

DRF序列化组件_DRF_37

DRF序列化组件_Django_38

DRF序列化组件_Django_39

DRF序列化组件_DRF_40

全局钩子:

DRF序列化组件_Django_41

总结:

DRF序列化组件_DRF_42

is_valid---》BaseSerializer的is_valid--》执行了self.run_validation(self.initial_data)---》Serializer的run_validation---》self.to_internal_value(data):局部钩子 或者
value = self.validate(value) :全局钩子

self.to_internal_value(data):局部钩子----getattr(self, 'validate_' + field.field_name, None)

3.序列化对象.data

入口:序列化对象.data方法

流程分析:

  • 序列化对象.data方法–》调用父类data方法—》调用对象自己的to_representation(自定义的序列化类无此方法,去父类找)
  • Serializer类里有to_representation方法,for循环执行attribute = field.get_attribute(instance)
  • 再去Field类里去找get_attribute方法,self.source_attrs就是被切分的source,然后执行get_attribute方法,source_attrs
  • 当参数传过去,判断是方法就加括号执行,是属性就把值取出来

图解:

DRF序列化组件_DRF_43

DRF序列化组件_Django_44

DRF序列化组件_Django_45

DRF序列化组件_Django_46

DRF序列化组件_DRF_47

DRF序列化组件_Django_48

DRF序列化组件_DRF_49

十四、子序列化

如果你在序列化器中想重写某个字段 (比如为Book的作者名都加上"xxx"后缀)

1.可以使用 source 来指定一个方法内存地址

# models.py 文件中在模型类中书写方法
def join_xxx(self,obj):
    return obj.name + "xxx"

# serializers.py 文件中对应的Book序列化类中重写字段指定方法
authors = serializer.CharField(source='join_xxx')

2.可以使用 SerializerMethodField 来操作

# serializers.py 文件中的Book序列化器书写
authors = serializers.SerializerMethodField()
def join_xxx(self,obj):
    return obj.name + "xxx"

3.可以使用子序列化来进行操作

  • 需要创建一个子序列化类
  • 子序列化类必须写在上方, 且只能对外键字段进行重写
  • 使用了子序列化的外键字段就不能再进行反序列化过程
  • 子序列化单个数据需填写 many=False, 多个数据需填写 many=True
# serializers.py 文件中创建子序列化类,并将子序列化类加入序列化类中

# 子序列化类必须写在上方
class AuthorSerializer(serializer.ModelSerializer):
    class Meta:
        model = models.Author
        fields = ["name"]
        
class BookSerializer(serializer.ModelSerializer):
    # 书写子序列化字段
    authors = AuthorSerializer(many=True, read_only=True)
    class Meta:
        model = models.Book
        fields = ["name","price","publish","authors"]