Welcome to kimi's blog

drf之序列化组件

drf之序列化组件

序列化与反序列化器

序列化器介绍和快速使用

  • 在写接口时,需要序列化和反序列化,而且反序列化的过程中要做数据校验,drf直接提供了固定的写法,只需要按照固定写法使用,就能完成上面的三个需求。
  • 提供了两个类 Serializer ModelSerializer,编写自定义的类,只需要继承drf提供的序列化类,就可以使用其中的某些方法,也能完成上面的三个需求

基于序列化器劣化和反序列化

序列化类的作用:做序列化,反序列化,反序列化校验
序列化过程:

  1. 写一个序列化类,继承Serializer,编写每个字段 CharField,字段表示要序列化和反序列化的字段
  2. 视图类中:导入Book,实例化得到对象books,对查出来的query对象们,对单个对象序列化并传入instance=books参数,如果是query是复数,一定要传many=True,query是单个对象,就不需要传入many
  3. 序列化类对象 : ser.data ``----> 字典或列表---通过Resonse`将json格式字符串返给前端
  4. 序列化单条和序列化多条,因为序列化单条需要传主键(PK),分别书写了两个路由,对应了两个视图类和两个get方法

序列化的基本使用

  1. 序列化多条

    serializer.py--BookSerializer类

    from rest_framework import serializers
    
    class BookSerializer(serializers.Serializer):
        # 序列化某些字段,这里写要序列化的字典
        name = serializers.CharField()  # serializers下大致跟models下的类是对应的
        price = serializers.CharField()
        publish = serializers.CharField()
    

    如果只是想序列化某几字段,就把不需要序列化的字段注释掉即可

    views.py--->BookView类

    class BookView(APIView):
        def get(self, request):
            # 验证原生request和新生的request
            print(request.method)  # GET
            print(request._request)  # <WSGIRequest: GET '/books/'>
            print(type(self.request))  # <class 'rest_framework.request.Request'>
    
            books = Book.objects.all()
            # 使用序列化类来完成---》得有个序列化类
            # instance要序列化的数据books queryset对象
            # many=True 只要是queryset对象要传many=True,如果是单个对象就不用传
            ser = BookSerializer(instance=books, many=True)
            return Response(ser.data)  # 无论是列表还是字典都可以序列化
    
  2. 序列化单条

    序列化类--默认

    from rest_framework import serializers
    
    class BookSerializer(serializers.Serializer):
        # 序列化某些字段,这里写要序列化的字典
        name = serializers.CharField()  
        price = serializers.CharField()
        publish = serializers.CharField()
    

    views.py--->BookDetailView类

    class BookDetailView(APIView):
        # def get(self, request,pk):  # 与下列方法时一样的,
        def get(self, request, *args, **kwargs):  # pk是以关键字传入的
            book = Book.objects.filter(pk=kwargs.get('pk')).first()
            # 序列化
            ser = BookSerializer(instance=book)
            return Response(ser.data)
    

    url 路由

    添加了新的路由

    urlpatterns = [
        path('books/<int:pk>/',views.BookDetailView.as_view())
    ]
    

反序列化基本使用

反序列化过程:新增,修改

    新增:
        1. 前端传入后端的数据,不论编码格式,都在request.data中,request.data
格式是字典
	   前端根据传入的编码格式不一样,从request.data取到的字典形式也是不一样的
	      编码格式                    字典
              urlencoded               QueryDict
              form-data                QueryDict
               json                       dict
        2. 将前端传入的数据request.data进行反序列化,并完成序列化类的反序列化
        3. 序列化类得到对象并传入参数:data=request.data
               校验数据
               保存:ser.save()--->序列化类中重写create方法
    
    修改:
        1. 拿到前端传入的数据,进行反序列化,查出要修改的对象--->序列化类的反序列化
        2. 序列化类得到对象,传入参数:instance=要修改的对象,data=request.data
               校验数据 
               保存:ser.save()  --->序列化类中重写update方法
  1. 序列化的新增

    序列化类

    
    class BookSerializer(serializers.Serializer):
        # 序列化某些字段,这里写要序列化的字典
        name = serializers.CharField()  
        price = serializers.CharField()
        publish = serializers.CharField()
    
        # 新增一条数据
        def create(self, validated_data):
            # 保存的逻辑
            # validated_data 校验过后的数据 {name,price,publish}
            # 保存到数据库
            book = Book.objects.create(**validated_data)
            # 一定不要忘记返回新增的对象
            return book
    

    视图类

    class BookView(APIView):
        def post(self, request):
            # requset.data  # 前端提交要保存的数据-->校验数据-->存数据
            ser = BookSerializer(data=request.data)  # 把前端传入的要保存的数据,给data参数
            # 校验数据
            if ser.is_valid():
                # 保存-->需要自己写,要在序列化类BookSerializer中写-->create方法
                ser.save()  # 调用ser.save,自动触发自定义编辑create方法保存数据
                return Response({'code': 100, 'msg': '新增成功', 'result': ser.data})
            else:
                return Response({'code': 101, 'msg': ser.errors})
            
            
       注释:
            print(ser)
                """BookSerializer(data=<QueryDict: {'name': ['cc'], 'price': ['123'], 'publish': ['上海']}>):
                    name = CharField()
                    price = CharField()
                    publish = CharField()
                    """
                print(ser.data)  # {'name': 'cc', 'price': '123', 'publish': '上海'}
    
  2. 反序列化的修改

    序列化类

    class BookSerializer(serializers.Serializer):
        # 序列化某些字段,这里写要序列化的字典
        name = serializers.CharField()  
        price = serializers.CharField()
        publish = serializers.CharField()
    
        def create(self, validated_data):
            book = Book.objects.create(**validated_data)
            return book
    	
        # 修改对象
        def update(self, instance, validated_data):
            # instance 要修改的对象
            # validated_data 校验过后的数据
            instance.name = validated_data.get('name')
            instance.price = validated_data.get('price')
            instance.publish = validated_data.get('publish')
            instance.save()  # orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库
    
            return instance  # 记得把修改的对象返回
    

    视图类

    class BookDetailView(APIView):
    
        def put(self, request, pk):
            book = Book.objects.filter(pk=pk).first()
            # 反序列化保存 ---借助于序列化类
            ser = BookSerializer(data=request.data, instance=book)
            if ser.is_valid():
                ser.save()  # 由于没有重写update,所以这报错
                return Response({'code': 100, 'msg': '修改成功', 'result': ser.data})
            else:
                return Response({'code': 101, 'msg': ser.errors})
    
  3. 删除单条

    视图类

    class BookDetailView(APIView):
        def delete(self,request,pk):
            Book.objects.filter(pk=pk).delete()
            return Response({'code':100,'msg':'删除成功'})
    

基于序列化器编写5个接口(重点)

url

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

urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/',views.BookView.as_view()),
    path('books/<int:pk>/',views.BookDetailView.as_view())
]

models.py

from django.db import models

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

serializer.py

from rest_framework import serializers
from .models import Book
# 多条
class BookSerializer(serializers.Serializer):
    # 序列化某些字段,这里泻药序列化的字典
    name = serializers.CharField()
    price = serializers.CharField()
    publish = serializers.CharField()


    # 新增一条数据
    def create(self, validated_data):
        # 保存的逻辑
        # validated_data 校验过后的数据 {name,price,publish}
        # 保存到数据库
        book=Book.objects.create(**validated_data)
        # 一定不要忘记返回新增的对象
        return book

    # 修改数据
    def update(self, instance, validated_data):
        # instance 要修改的对象
        # validated_data 校验过后的数据
        instance.name=validated_data.get('name')
        instance.price=validated_data.get('price')
        instance.publish=validated_data.get('publish')
        instance.save()  # orm的单个对象,修改了单个对象的属性,只要调用对象.save,就能把修改保存到数据库

        return instance # 不要忘了把修改后的对象,返回

view.py

from rest_framework.views import APIView,Response
from .models import Book
from django.http import JsonResponse
from django.core.handlers.wsgi import WSGIRequest
from rest_framework.request import Request

# 序列化多条
from .serializer import BookSerializer
class BookView(APIView):
     def get(self,request):
         # 只是为了验证之前讲过的
         print(request.method)
         print(request._request)
         print(type(self.request))

         books = Book.objects.all()

         # 使用自定义的序列化类来完成
         # instance要序列化的数据books queryset对象
         # many=True 只要是queryset对象要传many=True,如果是单个对象就不用传
         ser = BookSerializer(instance=books,many=True)
         return Response(ser.data)

		# 反序列化---新增
        def post(self,request):
        # requset.data  # 前端提交要保存的数据-->校验数据-->存数据
        ser = BookSerializer(data=request.data)
        # 检验数据
        if ser.is_valid():
            # 保存-->需要自己写,要在序列化类BookSerializer中写-->create方法
            ser.save()  # 调用ser.save,自动触发自定义编辑create方法保存数据
            # print(ser)
            """BookSerializer(data=<QueryDict: {'name': ['cc'], 'price': ['123'], 'publish': ['上海']}>):
                name = CharField()
                price = CharField()
                publish = CharField()
                """
            # print(ser.data)  # {'name': 'cc', 'price': '123', 'publish': '上海'}
            print(ser.errors)
            return Response({'code':100,'msg':'新增成功','result':ser.data})
        else:
            return Response({'code':101,'msg':ser.errors})


# 序列---单条
from .serializer import BookSerializer
class BookDetailView(APIView):
     def get(self,request,*args,**kwargs):
         book = Book.objects.filter(pk=kwargs.get('pk')).first()
         # 序列化
         ser =BookSerializer(instance=book)
         return Response(ser.data)

	# 反序列化---修改数据
    def put(self,request,pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data,instance=book)
        if ser.is_valid():
            ser.save()  # # 由于没有重写update,所以这报错
            return Response({'code':100,'msg':'修改成功','result':ser.data})
        else:
            return Response({'code':101,'msg':ser.errors})

    # 反序列化---删除数据
    def delete(self,request,pk):
        Book.objects.filter(pk=pk).delete()
        return Response({'code':100,'msg':'删除成功'})
    

反序列化的校验

序列化类反序列化的数据校验功能类比forms组件

  • 局部钩子
  • 全局钩子

代码实现

class BookSerializer(serializers.Serializer):
    name = serializers.CharField()  
    price = serializers.CharField()
    publish = serializers.CharField()
    
    # 新增一条数据
    def create(self, validated_data):
        book = Book.objects.create(**validated_data)
        return book
    
    # 修改一条数据
    def update(self, instance, validated_data):
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish = validated_data.get('publish')
        instance.save()
        return instance  # 保存
    
    # 反序列化校验的局部钩子,名字不能以h开头
    def validate_name(self,name):
        # 校验name是否合法
        if name.startswith('h'):
            # 校验不通过,抛异常
            raise ValidationError('不能以h开头')
        else:
            return name
     
   # 全局钩子
	def validate(self,attrs):
        # 校验过后的数据,书名和出版社的名字不能一致
        if attrs.get('name')==attrs.get('publish'):
            raise ValidationError('书名和出版社的名字不能一致')
        else:
            return attrs
	

序列化常用字段和字段参数(了解)

常用字段类

重点记忆是加粗字段

字段名 字段参数
CharField max_length=None, min_length=None, allow_blank=False, trim_whitespace=True
IntegerField max_value=None, min_value=None
FloatField max_value=None, min_value=None
BooleanField
NullBooleanField
FloatField max_value=None, min_value=None
DecimalField max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None 注:max_digits: 最多位数 decimal_palces: 小数点位置
TimeField format=api_settings.TIME_FORMAT, input_formats=None
DateField format=api_settings.DATE_FORMAT, input_formats=None
DateTimeField format=api_settings.DATETIME_FORMAT, input_formats=None)
EmailField max_length=None, min_length=None, allow_blank=False
RegexField regex, max_length=None, min_length=None, allow_blank=False
SlugField max_length=50, min_length=None, allow_blank=False),正则字段,验证正则模式 [a-zA-Z0-9-]+
URLField max_length=200, min_length=None, allow_blank=False
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 protocol=’both’, unpack_ipv4=False, **options)
DurationField
ChoiceField choices,choices与Django的用法相同
MultipleChoiceField choices
FileField max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL
ImageField max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL
ListField child=, min_length=None, max_length=None
DictField child=

常用字段参数

CharField及其子类的(EmailField)

反序列化的校验,字段自己的规则

参数 含义
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符

IntegerField

参数 含义
max_value 最小值
min_value 最大值

字段类共有的参数

参数 含义
required 表明该字段在反序列化时必须输入,默认True
default 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 该字段使用的验证器
error_messages 包含错误编号与错误信息的字典
label 于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
read_only 表明该字段仅用于序列化输出,默认False
write_only 表明该字段仅用于反序列化输入,默认False

序列化高级用法之source(了解)

首先先创建一个django项目drf_day04,创建Book表、Publish表以及Author表,并建立三个表之间的关系,完成模型表数据的迁移并录入数据

from django.db import models

# 创建关联表
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)

    # 外键 书与出版社 -对多,关联字段写在多的一方,写在Book
    publish=models.ForeignKey(to='Publish',on_delete=models.CASCADE)

    # 书与作者   多对多  需要建立中间表,django自动生成第三张表
    authors = models.ManyToManyField(to='Author')

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

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

image

序列化定制字段名字

sourse

  1. 可以定制序列化字段名
  2. 防止数据被人篡盗,将前端展示的字段名和后端数据的字段名设置成不同的字段名

sourse序列化自有字段和关联字段的区别

1.自有字段,直接写表字段名
         real_name = serializers.CharField(max_length=8,source='name')
2.关联字段  通过外键获取
    一对多  一对多的关联,直接点
        publish=serializers.CharField(max_length=8,source='publish.name')
    多对多  source不能用实现定制序列化关联表的字段
        authors = serializers.CharField(source='authors.all')

代码展示:

视图类:
class BookAPIView(APIView):
    def get(self,request):
        books = Book.objects.all()
        ser = BookSerializer(instance=books,many=True)
        return Response(ser.data)
    
定制序列化类
from rest_framework import serializers
# 序列化类
class BookSerializer(serializers.Serializer):
    # 字段参数 都可以通过sourse定制具体的字段名
    # 自有字段,直接写表字段名
    real_name = serializers.CharField(max_length=8,source='name')
    real_price = serializers.CharField(source='price')

    # 关联字段 一对多 ,直接通过外键点
    publish=serializers.CharField(max_length=8,source='publish.name')

    # 多对多,source不能用定制关联表的字段
    authors = serializers.CharField(source='authors.all')
    """
    1.我们序列化的是book表字段,自有字段名直接通过sourse指定的字段名-->>是Book表的字段名
    2.sourse定制目标的字段名name(max_length=8)...
    3.提高了安全性,后端真实的字段名:name、price,
    序列化给前端字段名:real_name、real_price,"authors": "<QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>"
    """

image

序列化高级用法之定制字段的两种方式

定制关联字段(publish/authors)的显示形式

  1. 一对多显示字典:{}

  2. 多对多显示列表套字典:[{},{},{}]

SerializerMethodField定制

返回给前端格式

    {
        "name": "红楼梦",
        "price": "11",
        "publish_detail": {"name": "北京出版社","addr": "北京"},
        "author_list": [{"name": "吴承恩","phone": "110"},{"name": "曹雪芹","phone": "120"}]
    }

高级序列化之SerializerMethodField

class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8)
    price = serializers.CharField()

    # 定制返回格式----SerializerMethodField
    publish_detail = serializers.SerializerMethodField()
    @propety # 可以伪装为方法的数据属性
    def get_publish_detail(self, obj):
        print(obj)
        return {'name': obj.publish.name, 'addr': obj.publish.addr}

    author_list = serializers.SerializerMethodField()
    @propety # 可以伪装为方法的数据属性
    def get_author_list(self, obj):
        l1 = []
        for author in obj.authors.all():
            l1.append({'name': author.name, 'phone': author.phone})
        return list

image

在表模型中定制

返回给前端格式

    {
        "name": "红楼梦",
        "price": "11",
        "publish_detail": {"name": "北京出版社","addr": "北京"},
        "author_list": [{"name": "吴承恩","phone": "110"},{"name": "曹雪芹","phone": "120"}]
    }

1.表模型
class Book(models.Model):
    name = models.CharField(max_length=32)
    price = models.CharField(max_length=32)
    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')
    
    # 定制在模型类中的方法
    def publish_detail(self):
        return {'name': self.publish.name, 'addr': self.publish.addr}
    
    def author_list(self):
        l1 = []
        for author in self.authors.all():
            l1.append({'name': author.name, 'phone': author.phone})
        return l1

2.序列化类
# 定制在模型类
class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8)
    price = serializers.CharField()

    # publish_detail = serializers.CharField()
    publish_detail = serializers.DictField()
    author_list = serializers.ListField()

image

总结

  1. 在序列化类中继承SerializerMethodField,类中编写一个get_字段名的(get_publish_detail)方法,该方法返回值=该字段值,该字段只能做序列化字段,反序列化不行
  2. 在表模型中使用编写:get_字段名的(get_publish_detail)方法,该publish_detail等于方法返回的值,序列化类中需要配合ListField,DictField两个字段类型---->该字段只能做序列化使用,不能实现做反序列化的字段
  3. 可以将上述的方法可以包装成数据属性---使用伪装@property

多表关联反序列化保存

##反序列化校验执行流程
1.先执行自有字段的校验(参数的控制)--->最大长度,最小长度,是否为空,是否必填,最小数字
2.validators=[方法,] ---->单独给这个字段加校验规则
    	name=serializers.CharField(validators=[方法,])
3.局部钩子校验规则
4.全局钩子校验规则

新增图书接口

前端页面传入的数据格式

{"name":"世界顶级思维","price":1000,"publish":1,"authors":[1,2]}

书写视图类中的post方法

class BookAPIView(APIView):
    def get(self,request):
        books = Book.objects.all()
        print(books)
        ser = BookSerializer(instance=books,many=True)
        return Response(ser.data)
	
    # 新增
    def post(self,request):
        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()  # 书写序列化类中create方法
            return Response({'code':100,'msg':'新增成功'})
        else:
            return Response({'code': 101, 'msg': ser.errors})

序列化类中,使用read_onlywrite_only分别定制序列化和反序列化的序列外键字段,并书写新增图书的create方法,read_onlywrite_only的目的是:需要序列化和反序列化的类,就可以使用这两种方法

from .models import Book
class BookSerializer(serializers.Serializer):
    # name和price,既可以序列化,又可以反序列化,即写又读,不用加read_only和write_only
    name = serializers.CharField(max_length=8)
    price = serializers.CharField()

    # 只用来做序列化  只读  read_only
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

    # 只用来反序列化  只写 write_only
    publish= serializers.CharField(write_only=True)
    authors= serializers.ListField(write_only=True)

    # 新增要重写create方法
    def create(self, validated_data):
        # validated_data 是校验过的数据
        # 新增一本图书
        book = Book.objects.create(name=validated_data.get('name'),price=validated_data.get('price'),publish_id=validated_data.get('publish'))

        # 作者也要关联上
        # book.authors  add remove set clear...
        book.authors.add(*validated_data.get('authors'))
        # book.authors.add(1,2)
        return book

image

修改图书接口

修改图书的接口跟新增图书不同,需要传主键值>>书写新的接口

path('books/<int:pk>/',views.BookDetailAPIView.as_view())

视图类:先查询(查询单条get方法)-----再修改(修改put方法)

class BookDetailAPIView(APIView):
    # 查询单条
    def get(self, request,pk):
        book = Book.objects.filter(pk=pk).first()
        print(book)
        ser = BookSerializer(instance=book)
        return Response(ser.data)


    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response({'code': 101, 'msg': ser.errors})

反序列化类

from .models import Book


class BookSerializer(serializers.Serializer):
    # name和price,既可以序列化,又可以反序列化,即写又读,不用加read_only和write_only
    name = serializers.CharField(max_length=8)
    price = serializers.CharField()

    # 只用来做序列化  只读  read_only
    publish_detail = serializers.DictField(read_only=True)
    author_list = serializers.ListField(read_only=True)

    # 只用来反序列化  只写 write_only
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)

    # 新增要重写create方法
    def create(self, validated_data):
		pass

    def update(self, instance, validated_data):
        # validated_data 校验过后的数据,{name:红楼梦,price:19,publish:1,authors:[1,2]}
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish_id = validated_data.get('publish')

        # 先清空多表关系的数据,再用add,否则会在后面追加,造成数据条重复
        authors = validated_data.get('authors')
        instance.authors.clear()
        instance.authors.add(*authors)
        instance.save()  # 保存
        return instance

image

反序列化字段校验其他

  1. 校验自有的字段,比如book中的name

    name = serializers.CharField(max_length=8,error_messages={'max_length:不能超过8位'})
    

image

  1. validators=[方法,] 忽略

  2. 局部钩子

        # 局部钩子
        def validate_name(self,name):
            if name.starswith('c'):
                raise ValidationError('不能以h开头')
            else:
                return name
    

    !image

  3. 全局钩子

    # 全局钩子
    def validate(self,attr):
        if attr.get('name')==attr.get('publish'):
            raise ValidationError('出版社名和书名不能一致')
            else:
                return attr
    

问题:反序列化的字段一定跟表模型的字段是对应的吗?
不一定
image

反序列化类校验部分源码解析(了解)

  1. 反序列化校验的开始

视图函数中ser.is_valid()被执行时,就会进行反序列化校验,校验通过返回True,否则返回False

  1. 反序列化的过程

    1. 1 ser.is_valid()`` 是序列化类的对象,假设序列化类是BookSerializer,当执行is_valid时,首先在序列化类产生的对象中查找,当找不到>>会找到父类BaseSerializer`中有 :【raise_exception:先注意】
     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)"""
    
    1. 2 不能直接按住ctrl键点击

      执行顺序是:从BookSerialier-->Serializer-->BaseSerializer-->Field
      BaseSerializer找到了is_valid-->执行self.run_validation(self.initial_data),我们直接点击是父类的run即(Field中的run_validation方法),而最终从下往上找,从Serializer类中找了run_validation(最终执行的方法)

image

  ```python
  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
  ```
  1. 3布局钩子的校验
  ```python
  # 局部钩子  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
  ```

ModelSerializer使用

ModelSerializer继承了Serializer,帮我们完成了很多的操作,比如跟表模型强关联,大部分的请求postput等,都不需要 在序列化里面书写createupdate方法了。

  1. fields='__all__'序列化所有Book中的字段

image

  1. fields= ['name','price'] 序列化所有Book中name和price字段名

image

image

  1. fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors'] 序列化所有Book中的name和price字段字段

如果meta里面的fields等于上面时,extra_kwargs只定制了name字段,查询出来的结果('name', 'price', 'publish_detail', 'author_list', 'publish', 'authors')如下

image

  1. 如果fields和extra_keargs都定制了如下图,查询所有的图书得到的结果('name', 'price', 'publish_detail', 'author_list')如下二图

image

image

总结

ModelSerializer 继承自Serializer,我们可以直接可以查询多条,单条,新增图书和修改图书等操作
针对fields=['name','photo','gender','addr'] 里面的参数要求:

  1. 只要是序列化的字段和反序列化的字段,都要在这注册 如:publish_detail,publish,authors
  2. 序列化的字段,可能不是表模型的字段,是自己写的方法
  3. 序列化的字段,可能是使用SerializerMethodField,也要注册
from .models import Book
from rest_framework.exceptions import ValidationError

class BookSerializer(serializers.ModelSerializer):
    # 跟表有关联
    class Meta:
        model = Book  # 跟book表建立了关系   序列化表和表模型类
        # fields = '__all__' # 序列化所有的Book中的字段,id name price,publish,authors
        # fields = ['name', 'price']  # 序列化所有Book中name和price字段名
        fields = ['name', 'price', 'publish_detail', 'author_list', 'publish', 'authors']  # 序列化所有Book中name和price字段名

        # 定制name 反序列化,最长不能超过8位,给字段类加属性----方法一
        extra_kwargs = {
            'name': {'max_length': 8},
            'publish_detail': {'read_only': True},
            'authors_list': {'read_only': True},
            'publish': {'write_only': True},
            'authors': {'write_only': True},
        }

    # 如果Meta写了__all__,就相当于复制了表模型中的字段,放在这里,做了映射
    # name = serializers.CharField(max_length=32)
    # price = serializers.CharField(max_length=32)

    # 定制name反序列化时,最长不能超过8,给字段类加属性--方式二 重写name字段
    # name = serializers.CharField(max_length=8)   # 如果重写name字段,校验的规则是>>>优先校验重写的name

    # 同理,所有的read_only和write_only 都可以通过重写或使用extra_kwargs传入


    # 终版,把这个序列化类跟之前一模一样的项目
    publish_detail = serializers.SerializerMethodField(read_only=True)

    def get_publish_detail(self, obj):
        return {'name': obj.publish.name, 'addr': obj.publish.addr}

    author_list = serializers.SerializerMethodField(read_only=True)
    def get_author_list(self, obj):
        list = []
        for author in obj.authors.all():
            list.append({'name': author.name, 'phone': author.phone})
        return list
    
    # 局部钩子和全局钩子跟之前完全一样
    def validate_name(self, name):
        if name.startswith('sb'):
            raise ValidationError('不能sb')

        else:
            return name

练习题

  1. 基于序列化器编写5个接口 Publish
  2. 原生的request没有data属性,如何实现一个原生的request.data,无论什么编码格式提交的数据提交都有值,
   FBV--->写个装饰器
   # 通过装饰器做,装饰器视图函数的,以后都会有request
def outer(func):
    def inner(request, *args, **kwargs):
        """
        request:新生的request
        urlencoded/form-data:request.POST就有值
        如果request.POST没有值说明前端传入的编码格式是json格式
        """
        try:
            print(request.body)
            request.data = json.loads(request.body)  # 表示是json格式遍布
        except Exception as e:
            request.data = request.POST
        res = func(request, *args, **kwargs)
        return res

    return inner


@outer  # func= outer(index_func)   执行index_func()>>>本质就是outer(index_func)(request)  >>> 本质就是inner(request)
def index_func(request):
    print(request.POST)  # urlencoded,form-data----->QueryDict
    print(request.data)  # 报错
    return HttpResponse('OK')

注意:根据前端传入的编码格式不一样,在打印request.data的操作之前,就做装饰器校验前端传入的数据格式做异常处理,如果传入的是json格式,将request.body拿到的数据>>>反序列化后并赋值给request.data;如果传入的是urlencoded/form-data ,我们通过request.POST就可以拿到值(字典)

  1. 继承Serializer的方式编写五个接口
    路由
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/',views.BookAPIView.as_view()),
    path('books/<int:pk>/',views.BookDetailView.as_view())
]

视图类

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


class BookAPIView(APIView):
    # 获取序列化多条
    def get(self, request):
        books = Book.objects.all()
        ser = BookSerializer(instance=books, many=True)
        return Response(ser.data)

    # 新增图书
    def post(self, request):

        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()  # 书写create方法
            return Response({'code': 100, 'msg': '新增成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class BookDetailView(APIView):
    # 获取单条
    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)

    # 修改图书
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()  # 书写update方法
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

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

序列化类

from rest_framework import serializers
from rest_framework.serializers import SerializerMethodField
from .models import Book


class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=8)
    price = serializers.CharField(max_length=8)

    # 定制在序列化类中
    publish_detail = serializers.SerializerMethodField()

    def get_publish_detail(self, obj):
        return {'name': obj.publish.name, 'address': obj.publish.address}

    author_list = serializers.SerializerMethodField()

    def get_author_list(self, obj):
        l1 = []
        for author in obj.authors.all():
            l1.append({'name': author.name, 'phone': author.phone,'gender':author.author_detail.gender})
        return l1

    # 反序列化
    publish = serializers.CharField(write_only=True)
    authors = serializers.ListField(write_only=True)

    # 新增图书
    def create(self, validated_data):
        book=Book.objects.create(name=validated_data.get('name'), price=validated_data.get('price'),
                            publish_id=validated_data.get('publish'))

        # 跟作者关联上
        book.authors.add(*validated_data.get('authors'))
        return book

    # 修改图书
    def update(self, instance, validated_data):
        print(instance)
        print(validated_data)

        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.publish_id = validated_data.get('publish')
        # 修改作者与图书的关系,先清楚原来的绑定关系
        instance.authors.clear()
        instance.authors.add(*validated_data.get('authors'))
        instance.save()
        return instance
  1. 继承继承ModelSerialize编写五个接口
    路由不变
urlpatterns = [
    path('admin/', admin.site.urls),
    path('books/',views.BookAPIView.as_view()),
    path('books/<int:pk>/',views.BookDetailView.as_view())
]

视图类不变

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


class BookAPIView(APIView):
    # 获取序列化多条
    def get(self, request):
        books = Book.objects.all()
        ser = BookSerializer(instance=books, many=True)
        return Response(ser.data)

    # 新增图书
    def post(self, request):

        ser = BookSerializer(data=request.data)
        if ser.is_valid():
            ser.save()  # 书写create方法
            return Response({'code': 100, 'msg': '新增成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})


class BookDetailView(APIView):
    # 获取单条
    def get(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(instance=book)
        return Response(ser.data)

    # 修改图书
    def put(self, request, pk):
        book = Book.objects.filter(pk=pk).first()
        ser = BookSerializer(data=request.data, instance=book)
        if ser.is_valid():
            ser.save()  # 书写update方法
            return Response({'code': 100, 'msg': '修改成功'})
        else:
            return Response({'code': 100, 'msg': ser.errors})

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

序列化类简化

class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book  # 与book表关联
        fields = ['name', 'price', 'get_publish_detail', 'get_author_list', 'publish', 'authors']  # 序列化字段

        # 定制字段
        extra_kwargs = {
            'name': {'max_length': 8},
            'publish_detail': {'read_only': True},
            'author_list': {'read_only': True},
            'publish': {'write_only': True},
            'authors': {'write_only': True}
        }
		
		
    # 定制在模型表中,注意下面的字段get_publish_detail要和fields的里面的字段名要一致
    def get_publish_detail(self):
        return {'name': self.publish.name, 'address': self.publish.address}

    def get_author_list(self):
        l1 = []
        for author in self.authors.all():
            l1.append({'name': author.name, 'phone': author.phone,'gender':author.author_detail.gender})
        return l1

posted @   魔女宅急便  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
Title
点击右上角即可分享
微信分享提示

目录导航