Django REST framework - 序列化组件

序列化器-Serializer

什么是rest_framework序列化?

在写前后端不分离的项目时:

  我们有form组件帮我们去做数据校验

  我们有模板语法,从数据库取出的queryset对象不需要人为去转格式

当我们写前后端分离项目的时:

  我们需要自己去做数据校验

  我们需要手动去转数据格式,因为跨平台数据传输都用json字符串,不能直接jsonqueryset对象

序列化器的作用

1 序列化:把python中的对象转成json格式字符串
        序列化器会把模型对象转换成字典,经过response以后变成json字符串
2 反序列化:把json格式字符串转成python中的对象
        把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
​
3 注意:drf的序列化组件(序列化器)
    把对象(Book,queryset对象)转成字典
    因为有字典,直接丢到Response中就可以了

序列化,反序列化
    -对象----》json
    -json---》对象
序列化器
    -定义一个类,继承Serializer
    -在类内些字段(常用字段,和非常用字段)(字段参数)
    -在视图类中,实例化得到一个序列化类的对象,传入要序列化的数据
    -对象.data---》就是字典
    -source

序列化器的基本使用

from rest_framework.serializers import Serializer,ModelSerializer
from rest_framework import serializers

Serializer是rest_framework原生的序列化组件
ModelSerializer是rest_framework在原生的序列化组件的基础上封装了一层的序列化组件

用法:1、在用我们的rest_framework序列化组件的时候,我们的视图层都必须写视图类,不能再写视图函数

   2、我们需要针对每一张模型表写一个类来继承Serailizer或者ModelSerailizer类,

 注:当我们在视图类里需要对数据进行序列化或者反序列化的时候,在自己定义的类传入需要序列化的数据实例化,

   调用.data即可拿到序列化或者校验后的数据了

Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serializer

1 写一个序列化的类,继承Serializer
class BookSerializer(serializers.Serializer):
    # 在这里写要序列化的字段
    # 序列化字段类(有很多,常用的就几个,等同于models中的字段类)
    # 字段类,有很多字段参数()
    name = serializers.CharField()
    price = serializers.IntegerField()
    # publish = serializers.CharField()
2 在类中写要序列化的字段(常用字段,和非常用字段)(字段参数)
    name = serializers.CharField()
    price = serializers.IntegerField()
3 在视图类中使用(实例化得到一个序列化类的对象,传入要序列化的数据)
    class BookView(APIView):
    def get(self, request):
        book_list = models.Book.objects.all()
        # instance=None, 要序列化的数据
        # data=empty  ,要反序列化的数据(目前data先不用)
        # many=True 如果序列化多条,一定要写many=True
        book_ser = BookSerializer(instance=book_list, many=True)
        # book_ser.data就是序列化后的数据
        return Response(book_ser.data)
4 得到序列化后的数据,返回(对象.data---》就是字典)
    book_ser.data
    
5 字段参数,source,指定要序列化表中的哪个字段

路由层 urls.py

from django.urls import path
from app import views


urlpatterns = [
    path('admin/', admin.site.urls),
    # 获取所有的书籍信息
    path('books_new/', views.BookView.as_view()),
    # 对某本书进行操作
    path('books_new/<int:id>/', views.BookViewId.as_view()),
  

]

模型层 models.py

from django.db import models

class Book(models.Model):
    name = models.CharField(max_length=32, verbose_name='书名')
    publish = models.CharField(max_length=32, verbose_name='出版社')
    price = models.IntegerField(verbose_name='价格')

序列化器层 serializer.py

from rest_framework import serializers
from app import models


# 新建序列化类,继承Serializer
class BookSerializer(serializers.Serializer):
    # 类中定义和模型表一一对应的字段,在这里写要序列化的字段
    # 序列化字段类(有很多,常用的就几个,等同于models中的字段类)
    # 字段类,有很多字段参数()
    name = serializers.CharField()
    price = serializers.IntegerField()
    publish = serializers.CharField()

视图层 views.py

# 获取所有书籍信息
class BookView(APIView):
    def get(self, request):
        book_list = models.Book.objects.all()
        book_ser = BookSerializer(instance=book_list, many=True)
        # book_ser.data就是序列化后的数据
        return Response(book_ser.data)

"""
  instance=None, 要序列化的数据
  data=empty  ,要反序列化的数据(目前data先不用)
  many=True 如果序列化多条,一定要写many=True
"""
# 对某一本书进行操作
class BookViewId(APIView):
    # 对某一个进行操作需要带上id
    def get(self, request, id):
        book = models.Book.objects.all().filter(pk=id).first()
        book_ser = BookSerializer(instance=book)
        return Response(book_ser.data)

source

1 指定要序列化的字段(数据表中字段)
  publish = serializers.CharField(source='publish.city') # 拿到出版社所在的城市
2 指定方法
3 深度表查询
4 用的最多:只有一个字段(也可以跨表)

SerializerMethodField

1 用的最多,跨表查(要么是列表,要么是字典)
publish=serializers.SerializerMethodField()
def get_publish(self,obj):
        return {'name':obj.publish.name,'city':obj.publish.city}

authors = serializers.SerializerMethodField()
def get_authors(self, obj):
       return [{'name':author.name, 'age': author.age} for author in obj.authors.all()]

在模型表中写方法

# 表模型中写的
def publish_name(self):
   return {'name':self.publish.name,'city':self.publish.city}

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

#序列化类中
# publish = serializers.CharField(source='publish_name')
publish_name = serializers.DictField()
author_list=serializers.ListField()

序列化类常用字段类型及属性

常用字段类型:

字段参数针对性分类

# 针对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
error_messages    包含错误编号与错误信息的字典

validators    该字段使用的验证器(了解)

反序列化、局部和全局钩子

使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。

在获取反序列化的数据前,必须调用is_valid()方法进行验证,验证成功返回True,否则返回False。

验证失败,可以通过序列化器对象的errors属性获取错误信息,返回字典,包含了字段和字段的错误。如果是非字段错误,可以通过修改REST framework配置中的NON_FIELD_ERRORS_KEY来控制错误字典中的键名。

验证成功,可以通过序列化器对象的validated_data属性获取数据。

在定义序列化器时,指明每个字段的序列化类型和选项参数,本身就是一种验证行为。

反序列化

1 如果要反序列化,继承了Serializer,必须重写create方法

2 使用
# 视图类
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('数据有问题啊')
    
# 序列化类
def create(self, validated_data):
    # 校验过后的数据
    res = models.Publish.objects.create(**validated_data)
    return res
"""
父类的save内部调用了create,所以我们重写create
return res 给了self.instance以后,instance就有值了
publish_ser.data,instance就有值调用data就能拿到序列化后的数据

is_valid()方法还可以在验证失败时抛出异常serializers.ValidationError,可以通过传递raise_exception=True参数开启,
REST frame

局部和全局钩子

# validate_字段名    
    def validate_name(self, data):
        # data就是当前字段的值
        if data.startswith('sb'):
            raise ValidationError('不能以sb开头')
        else:
            return data
        
# 在序列化器中需要同时对多个字段进行比较验证时,可以定义validate方法来验证
    def validate(self, attrs):
        if attrs.get('name') == attrs.get('city'):
            raise ValidationError('city和名字不能一样')
        else:
            return attrs

模型序列化全

视图类

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


class BookViews(APIView):
    # 查询所有
    def get(self, request, *args, **kwargs):
        response = {'status': 200, 'msg': '查询'}
        try:
            # 取出数据库所有
            book_s = models.Book.objects.all()
            # 序列化全部
            books = serializer.BookModelSerializer(instance=book_s, many=True)  # many全部
            # book.data字典加入到data列表里
            response['data'] = books.data
        except Exception as e:
            # 返回错误信息
            response['msg'] = str(e)
        return Response(response)

    # 增加
    def post(self, request):
        response = {'status': 200, 'msg': '新增'}
        try:
            # data反序列化
            book_ser = serializer.BookModelSerializer(data=request.data)
            # 校验
            if book_ser.is_valid():
                # 保存
                book_ser.save()
                # book.data字典加入到data列表里
                response['data'] = book_ser.data
            else:
                # book.error错误信息加入到msg列表里
                response['msg'] = book_ser.errors
        except Exception as e:
            # 返回错误信息
            response['msg'] = str(e)
        return Response(response)


class BooksViewsId(APIView):
    # 查询一个
    def get(self, request, id):
        response = {'status': 200, 'msg': '查询'}
        try:
            # 取出id
            book_s = models.Book.objects.all().filter(pk=id).first()
            # 序列化
            book_k = serializer.BookModelSerializer(instance=book_s)
            # book.data字典加入到data列表里
            response['data'] = book_k.data
        except Exception as e:
            # 返回错误信息
            response['msg'] = str(e)
        return Response(response)

    # 修改数据
    def put(self, request, id):
        response = {'status': 200, 'msg': '修改'}
        try:
            # 取出id
            books_list = models.Book.objects.all().filter(pk=id).first()
            # 拿到id号反序列化
            book_ser = serializer.BookModelSerializer(instance=books_list, data=request.data)
            # 校验
            if book_ser.is_valid():
                # 保存
                book_ser.save()
                # book.data字典加入到data列表里
                response['data'] = book_ser.data
            else:
                # book.error错误信息加入到msg列表里
                response['msg'] = book_ser.errors
        except Exception as e:
            # 返回错误信息
            response['msg'] = str(e)
        return Response(response)
    
    # 删除
    def delete(self, request, id):
        # 查询到id号
        res = models.Book.objects.filter(pk=id).delete()
        # 判断第一个删除
        if res[0] > 0:
            return Response('')
        else:
            return Response('删除的不存在')

序列化类

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


# 继承ModelSerializer类
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        # __all__ 查询所有
        fields = '__all__'
        # 字段限制
        extra_kwargs = {
            'name': {'max_length': 6, 'required': True},
            'price': {'min_value': 0, 'required': True},
        }

    # 局部钩子
    def validate_name(self, data):
        # data就是当前字段的值
        if data.startswith('sb'):
            raise ValidationError('不能以sb开头')
        else:
            return data

    # 全局钩子
    def validate(self, attrs):
        if attrs.get('name') == attrs.get('price'):
            raise ValidationError('名字一样')
        else:
            return attrs

    # 重写字段
    publish = serializers.SerializerMethodField()
    # 字段详情,必须加get_字段名方法
    def get_publish(self, obj):
        return {'name': obj.publish.name, 'city': obj.publish.city}

    #  重写字段
    authors = serializers.SerializerMethodField()  
    # 字段详情,必须加get_字段名方法
    def get_authors(self, obj):
        return [{'name': author.name, 'age': author.age} for author in obj.authors.all()]

路由

from restful import views
urlpatterns = [
path('books/<int:id>/', views.BooksViewsId.as_view()),
path('booka/', views.BookViews.as_view()),
]

settings配置app(注意记得配置数据库也可以用自带sqllite3)

INSTALLED_APPS = [
    'rest_framework'
]

models 表(注意迁移)

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_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

admin 配置(注意创建用户 createsuperuser)

from django.contrib import admin
from restful import models


admin.site.register(models.Book)
admin.site.register(models.Author)
admin.site.register(models.AuthorDatail)
admin.site.register(models.Publish)

序列化类源码分析

序列化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

1 Request:新的request
2 常用属性

1).data
request.data 返回解析之后的请求体数据。类似于Django中标准的request.POST和 request.FILES属性,但提供如下特性:

包含了解析之后的文件和非文件数据
包含了对POST、PUT、PATCH请求方式解析后的数据
利用了REST framework的parsers解析器,不仅支持表单类型数据,也支持JSON数据
2).query_params
request.query_params与Django标准的request.GET相同,只是更换了更正确的名称而已

响应对象 Response

#一直会用
data: 为响应准备的序列化处理后的数据(字典)
#偶尔会用
headers: 用于存放响应头信息的字典;
status: 状态码,默认200;(http请求的状态码)
    
# 基本不用  
template_name: 模板名称,如果使用HTMLRenderer 时需指明;
content_type: 响应数据的Content-Type,通常此参数无需传递,REST framework会根据前端所需类型数据来设置该参数。

总结

0 drf是django的一个app
    -序列化器
    -Request
    -Response
    -版本控制
    -认证,频率,权限
    -过滤,排序,分页
    -视图相关
    -自动生成接口文档(coreapi,swagger)
    -jwt做认证(第三方)
    -xadmin---》对admin的美化(bootstrap+jq,1.x版本),simple-ui
    -RBAC:基于角色的访问控制(公司内部项目)
    
    
1 序列化器
    1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
    2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
    3. 反序列化,完成数据校验功能
    
2 Serializer
    -序列化
        -实例化序列化对象,many参数作用
        -source
        -SerializerMethodField
        -模型表中写方法    
    -反序列化
        -ser=BookSerializer(data=request.data)
        -数据校验:ser.is_valid()
        -存数据:手动存,重写BookSerializer的create方法,update方法
        -ser.save() 如果是新增,会调用create,如果是修改,会调用update
        -ser.data 如果有instance对象,就是对instance做序列化
        -全局,局部钩子,字段参数
3 ModelSerializer
    -class Meta:
        model=Book
        fields=‘__all__’
        extra_kwargs
    -重写某个字段:跟之前学的又一样了
    -子序列化
4 常用非常用字段
5 字段参数
6 如果第三张表是手动建的,authors是存不进去的
    {
    name:ddd
    price:10
    publish:1
    authors:[1,2]
    }
    
7 请求对象的属性
8 响应对象
    -data:响应数据
    -status:响应状态码
    -header:响应头
    -content_type:响应类型

9 加入serializer后整个后端逻辑
posted @ 2021-12-06 11:39  沈忻凯  阅读(80)  评论(0)    收藏  举报