序列化器与请求响应

序列化器的作用

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

注意: drf的序列化组件(序列化器)
把对象(Bookqueryset对象)转成字典因为有字典直接使用Response就可以了

序列化器的使用

1. 写一个序列化类继承Serializer
	 from rest_framework import serializers
   class BookSerializer(serializers.Serializer):
      # 这里写要序列化的字段 类似于models中的字段
      name = serializers.CharField()
      publish = serializers.CharField()
      price = serializers.IntegerField()

2. 在是视图类中使用
   book_list = models.Books.objects.all()
   book_ser = BookSerializer(instance=book_list, many=True)

3. 得到序列化后的数据然后返回
   return Response(book_ser.data)

'''
要写5个接口
1 获取所有  http://127.0.0.1:8000/books_new/    get
3 新增一个 http://127.0.0.1:8000/books_new/     post
2 获取单个 http://127.0.0.1:8000/books_new/1/   get
4 修改一个 http://127.0.0.1:8000/books_new/1/   put
5 删除
'''
# 可以多写类 根据ID获取值
class BookViewId(APIview):
    def get(self, request, id):
      book = models.Book.object.all().filter(pk=id).first()
      book_ser = BookSerializer(instance=book)
      return Response(book_ser.data)

# urls
path('books/<int:id>/', views.BookViewId.as_view()),

source

1. 指定要序列化表中的哪个字段
   pub = serializers.CharField(source='publish')
   # 将表中的 publish 取出给 pub
   # source 后的字段与前面的字段不要重复

2. 还可以跨表查出
   publish = serializers.CharField(source='publish.city')

SerializerMethodField

'''
要求返回的字段内是字典
publish = {'name': 名字, 'city':城市, 'email': 邮箱}
'''
# serializer.py
from rest_framework import serializers
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}

'''
[
    {
        "name": "三国演义",
        "price": 35,
        "publish": {
            "name": "北方出版社",
            "city": "北京",
            "email": "[email protected]"
        }
    }
'''


# 返回的结果中又有多组数据的话使用如下方法
  authors = serializers.SerializerMethodField()
  def get_authors(self, obj):
      return [{'id': author.nid, 'name': author.name, 'age': author.age} for author in obj.authors.all()]

'''
[
    {
        "name": "水浒传",
        "price": 45,
        "publish": {
            "name": "南方出版社",
            "city": "南京",
            "email": "[email protected]"
        },
        "authors": [
            {
                "id": 1,
                "name": "xxx",
                "age": 18
            },
            {
                "id": 2,
                "name": "yyy",
                "age": 20
            }
        ]
    }
]
'''

在模型表中写方法

# 表模型中的写法
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 publish_name(self):
        return {'name': self.publish.name, 'city': self.publish.city}  # 返回的是一个字典
    @property
    def author_list(self):
        return [{'id': author.nid,'name': author.name, 'age': author.age} for author in self.authors.all()]

# 序列化类中的写法
  publish_name = serializers.DictField()  # 接收的是一个字典  需要使用 DictField() 返回出去
  author_list = serializers.ListField()   # 接收的是一个列表  需要使用 ListField() 返回出去

序列化类常用字段和属性

常用字段属性

img

img

属性分类

# 针对CharField
max_length   最大长度
min_length   最小长度
allow_blank  是否允许为空

# 针对InterField
max_value  最大值
min_value  最小值

# 通用
read_only   表明该字段仅用于序列化输出默认False(序列化)
write_only  表明该字段仅用于反序列化输入默认False(反序列化)

required   表明该字段在反序列化时必须输入默认True
default    反序列化时使用的默认值
allow_null 表明该字段是否允许传入None默认False
error_messages  包含错误编号与错误信息的字典

validators  该字段使用的验证器(了解)
# 序列化类
class PublishSerializer(serializers.Serializer):
    nid = serializers.CharField(required=False)
    # error_messages 和forms组件中使用类似
    name = serializers.CharField(max_length=3, error_messages={'max_length': '名字太长了'})
    city = serializers.CharField()
    email = serializers.EmailField()

# 视图类
def post(self, request):
    publish_ser = serializer.PublishSerializer(data=request.data)
    if publish_ser.is_valid():
        publish_ser.save()
        return Response(publish_ser.data)
    else:
        print(publish_ser.errors)
        return Response(publish_ser.errors)  # 此时报错信息就是 '名字太长了'

反序列化

如果要反序列化继承了Serializer必须重写createupdated等方法

"""
父类的save内部调用了create,所以我们重写create
return res 给了self.instance以后,instance就有值了
publish_ser.data,instance就有值调用data就能拿到序列化后的数据

is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,
REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。
"""

视图类

from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import serializer
from app01 import models

class PublishView(APIView):
    def get(self, request):
        publish_list = models.Publish.objects.all()
        publish_ser = serializer.PublishSerializer(instance=publish_list, many=True)
        return Response(publish_ser.data)

    def post(self, request):
        publish_ser = serializer.PublishSerializer(data=request.data)
        if publish_ser.is_valid():
            publish_ser.save()
            return Response(publish_ser.data)
        else:
            print(publish_ser.errors)
            return Response('数据存在问题')


class PublishDetailView(APIView):
    def get(self, request, id):
        publish_list = models.Publish.objects.filter(pk=id).first()
        publish_ser = serializer.PublishSerializer(instance=publish_list)
        return Response(publish_ser.data)

    def put(self, request, id):
        publish_list = models.Publish.objects.filter(pk=id).first()
        publish_ser = serializer.PublishSerializer(instance=publish_list, data=request.data)
        if publish_ser.is_valid():  # 校验数据
            publish_ser.save()  # 保存数据
            return Response(publish_ser.data)
        else:
            return Response('数据校验错误')

    def delete(self, request, id):
        res = models.Publish.objects.filter(pk=id).delete()
        if res[0] > 0:
            return Response('')
        else:
            return Response('数据不存在')

序列化类

from rest_framework import serializers
from app01 import models

class PublishSerializer(serializers.Serializer):
    nid = serializers.CharField(required=False)
    name = serializers.CharField()
    city = serializers.CharField()
    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):
        # 手动将值取出,再存入表中
        name = validated_data.get('name')
        city = validated_data.get('city')
        email = validated_data.get('email')
        instance.name = name
        instance.city = city
        instance.email = email
        instance.save()
        return instance  # 需要将 instance 返回

表模型

from django.db import models
class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()
    def __str__(self):
        return self.name

路由

from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('publish/', views.PublishView.as_view()),
    path('publish/<int:id>/', views.PublishDetailView.as_view()),
]

局部、全局钩子

# 序列化类
from rest_framework import serializers
from app01 import models
from rest_framework.exceptions import ValidationError


class PublishSerializer(serializers.Serializer):
    nid = serializers.CharField(required=False)
    name = serializers.CharField()
    city = serializers.CharField()
    email = serializers.EmailField()
    # 局部钩子
    def validate_name(self, data):
        # data 就是当前字段的值
        if data.startswith('sb'):
            raise ValidationError('不能以sb开头')  # 抛出异常  publish_ser.errors
        else:
            return data
    # 全局钩子
    def validate(self, attrs):
        if attrs.get('name') == attrs.get('city'):
            raise ValidationError('名称和城市名不能一致')
        else:
            return attrs

模型类序列化器

视图类

from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import serializer
from app01 import models


class BookView(APIView):
    def get(self, request):
        qs = models.Book.objects.all()
        ser = serializer.BookModelSerializer(instance=qs, many=True)
        return Response(ser.data)

    def post(self, request):
        ser = serializer.BookModelSerializer(data=request.data)
        if ser.is_valid():
            # ModelSerializer 重写了save 方法 不用手动
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)


class BookDetailView(APIView):
    def get(self, request, id):
        book = models.Book.objects.filter(pk=id).first()
        ser = serializer.BookModelSerializer(instance=book)
        return Response(ser.data)

    def put(self, request, id):
        book = models.Book.objects.filter(pk=id).first()
        ser = serializer.BookModelSerializer(instance=book, data=request.data)
        if ser.is_valid():
            ser.save()
            return Response(ser.data)
        else:
            return Response(ser.errors)

    def delete(self, request, id):
        res = models.Book.objects.filter(pk=id).delete()
        if res[0] > 0:
            return Response('')
        else:
            return Response('数据不存在')

序列化类


class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'  # 全部字段
        # fields = ['name', 'price']  # 指定字段
        # exclude = ['name']   # 排除字段
        # depth = 2  # 跨几个表的详细数据,一般不用,官方建议最多到10, 一般不超过3

        # 某个字段用的字段类,取决于models中的字段类
        # 在某个字段上加属性
        extra_kwargs = {
            'name': {'max_length': 6, 'required': True},
            'price': {'min_value': 0, 'required': True},
            'publish': {'required': True, 'write_only': True},
            'authors': {'required': True, 'write_only': True}
        }
		# publish和authors两个字段只需要在写入时写入,而在请求时不会返回
    # 而是由下面的publish_detail authors_list字段显示

    # 把publish详情显示
    # 重写publish字段
    # publish = serializers.CharField()

    # 可以加一个不存在的字段
    # aa = serializers.CharField(source='name')

    # 之前的方法也可以使用

    # 子序列化类
    publish_detail = PublishSerializer(source='publish', read_only=True)
    authors_list = serializers.ListField(read_only=True)
    # 这两个read_only只会在get的时候显示  写入时不需要传入

    # 局部钩子、 全局钩子

表模型

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

    def publish_list(self):
        return {'name': self.publish.name, 'city': self.publish.city}

    @property
    def authors_list(self):
        return [{'id': author.nid, 'name': author.name, 'age': author.age} for author in self.authors.all()]


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

    def __str__(self):
        return self.name


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

    def __str__(self):
        return self.addr

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

    def __str__(self):
        return self.name

路由

from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
    path('admin/', admin.site.urls),
    path('publish/', views.PublishView.as_view()),
    path('books/', views.BookView.as_view()),
    path('publish/<int:id>/', views.PublishDetailView.as_view()),
]

下图就是read_onlywrite_only的作用,写入的和显示的可以不一致

image-20220316204027223

序列化源码分析

many参数

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的对象

局部钩子和全局钩子

is_valid()
判断_validated_data如果没有
执行了 self.run_validation(self.initial_data)
目前在BaseSerializer如果按住ctrl点击会直接进到它父类的run_validation进到Field不是真正执行的方法

我们需要从头找实际上是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)



## 总结:
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)

序列化对象.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
当参数传过去判断是方法就加括号执行是属性就把值取出来

请求与响应

请求

# Request 类的对象---》新的request对象
from rest_framework.request import Request

# 记住的
	__getattr__
	request.data
  request.query_parmas--->self._request.GET-->restful规范里请求地址中带过滤(查询)条件---> get请求地址中提交的数据在GET中 --> query_parmas查询参数


# 了解--->默认情况下,可以解析 urlencoded,formdata,json
	-如果我们写了一个接口想只能处理json格式或者只能处理formdata

# from rest_framework import settings DRF内的所有配置的位置

# 局部配置
from rest_framework.parsers import JSONParser,FormParser,MultiPartParser
class PublishView(APIView):
    # 局部使用,只针对当前视图类有效,只想处理json格式
    parser_classes = [JSONParser]

# 全局配置---> 配置文件---> 所有接口都只能解析json格式
REST_FRAMEWORK = {
    'DEFAULT_PARSER_CLASSES': [
        # 解析json格式数据({"user": "zhubao"}),解析成功后,request.data类型为dict
        'rest_framework.parsers.JSONParser',
        # 解析form表单格式数据(user=zhubao),解析成功后,request.data类型为QuerySet
        'rest_framework.parsers.FormParser',
        # (用的较少),解析较为复杂的form表单格式数据,解析成功后,request.data类型为QuerySet
        'rest_framework.parsers.MultiPartParser'
    ]
}

# 全局配置解析json,局部某个视图函数想能解析formdata格式
视图类中配置一下即可--->局部配置
  如果局部配置如下:
  parser_classes = [] # 所有格式都补不能解析了

# 使用顺序:我们没有配置,也有默认配置:3个都能解析
	drf有默认配置(最后) ----> 项目配置文件的配置(其次) ----> 视图类中配的(优先用)
  drf的默认配置from rest_framework import settings

# 总结:一般情况下,都使用默认即可,不用配置

响应

Respone:from rest_framework.response import Response

# 属性
data=None,    # 字符串,字典,列表--》给http响应body体中内容-->response对象中取出处理
status=None,  # 响应状态码:1xx,2xx,3xx,默认是200
from rest_framework.status import HTTP_201_CREATED
Response(ser.data,status=HTTP_201_CREATED)

headers=None,      # 响应头 字典

---了解---
template_name=None,  # 模板名字(不用),用浏览器访问时,可以改
exception=False,    # 异常处理
content_type=None   # 响应编码格式


# 响应格式---> 跟解析格式

# 局部设置
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class BookDetailView(APIView):
    renderer_classes = [JSONRenderer,]

# 全局设置
REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (  # 默认响应渲染类
        'rest_framework.renderers.JSONRenderer',  # json渲染器
        'rest_framework.renderers.BrowsableAPIRenderer',  # 浏览API渲染器
    )
}

自定义 Response 对象

# 在app目录下手动创建response.py文件,名字随意
# 自定义一个APIResponse类,继承drf的Response
from rest_framework.response import Response
class APIResponse(Response):
    def __init__(self, code=100, msg=None, data=None, status=None,
                 template_name=None, headers=None,
                 exception=False, content_type=None, **kwargs):
        dic = {'status': code, 'msg': msg}
        if data: # 如果data有值,说明要往里面放东西
            dic['data'] = data
        if kwargs: # 自定义传的参数会添加到字典里
            dic.update(kwargs)

        super().__init__(data=dic, status=status,
                         template_name=template_name, headers=headers,
                         exception=exception, content_type=content_type)


### 使用,在视图类中from app01.response import APIResponse # 先导入自定义的类
return APIResponse(msg='成功了',data={'name': 'lqz', 'age': 19},next=9)