DRF从入门到精通二(Request源码分析、DRF之序列化、反序列化、反序列化校验、序列化器常用字段及参数、source、定制字段、保存数据)

文章目录

  • 一、Request对象源码分析
    • 区分原生request和新生request
    • 新的request还能像原来的reqeust一样使用吗
    • 源码片段分析
    • 总结:
  • 二、DRF之序列化组件
    • 序列化介绍
    • 序列化步骤
    • 序列化组件的基本使用
    • 反序列化基本使用
      • 反序列化的新增
      • 反序列化的新增
      • 删除单条
    • 反序列化的校验
    • 序列化器常用字段和字段参数
      • 常用字段类
      • 常用字段参数
    • 序列化器-source的使用
      • source序列化自有字段和关联字段的区别
    • 定制字段的两种方式
    • 反序列化之保存数据
      • 新增
      • 修改
    • 反序列化之数据校验
    • ModelSerializer的使用
      • 使用方法
      • 总结

一、Request对象源码分析

在上一篇博客中最后分析APIView时,我们分析出继承了APIView的视图,以后request都是新的request了,是DRF提供的Request的对象

区分原生request和新生request

	原生request:django.core.handlers.wsgi.WSGIRequest

	新生request:from rest_framework.request import Request

	原生request可以在新生request._request中获取

新的request还能像原来的reqeust一样使用吗

	用起来屎一样的,新的request只是在原来的request基础上添加了一些功能,并不影响基础功能的使用,其本质就是类似装饰器
	 print(request.method)  # get
     print(request.path)  # /movies/
     print(request.GET)   # 原来的get请求提交的参数
     print(request.POST)  # 原来post请求提交的参数

源码片段分析

	'''源码解析之 __init__'''
	从上面区分原生request和新的request中可以知道,老的request对象其实就是在新的内部,request._request中
	'我们先看__init__,它是一个初始化方法,类实例化得到对象时,会执行它,然后会往对象中放数据'
	
	def __init__(self, request, parsers=None, authenticators=None,
                 negotiator=None, parser_context=None):
		
		'''
		传入的request是老的,django原生的request
		放到self._request,self是新的request类的对象
		这里它把传入过来的原request放到这个self._request中,这里的self已经不是视图类了,
		因为这个Request类没有继承任何一个类,它就是它自己,所以这个self是Request
		'''
        self._request = request
        self._data = Empty
        self._files = Empty
        .....

在类内部,以 __开头 __结尾的方法, 在某种情况下会自动调用,他们称之为魔法方法。具体有那些,可以自行搜索,因为所有的类都继承了Object类,所以也可以在Object类中看看,但是不全里面

在这里插入图片描述

	'''源码解析之__getattr__'''
	类中有个魔法方法:__getattr__,对象.属性,属性不存在会触发它的执行
   def __getattr__(self, attr): 如果取的属性不存在会去原生的Django的request对象中取出来
	   try:
 '通过反射,因为这里是self._request所以去Django的request取,能取到就返回,娶不到就执行except代码,如果还取不到则报错'
	        return getattr(self._request, attr)
	    except AttributeError:
	        return self.__getattribute__(attr)
	 '以后用的所有属性或方法,直接用就可以了(通过反射去原来的request中取)' 

以后新的request中多了一个属性data,它会把前端post/put提交的请求体中的数据,都放在request.data中,无论何种编码格式,它都是字典

	data是一个方法,被property装饰了,变成了数据属性用
		-以后body中提交的数据,都从这里取(request.POST)
		-urlencoded,form-data:提交的数据在request.POST中
		-json格式提交的数据,在request.POST中是没有的,它在request.body中
		-现在无论那种格式,都可以直接从request.data中取
	
	request.query_params:get请求提交的参数,以后从这里取
	
	request.FILES:取文件就还是从这个里面取,和之前一样

总结:

	1.新的request跟之前的用法一模一样,如果新的request取不到,它使用__getattr__魔法方法去原生request中取。
		当然原生的也可以直接在新的request中拿到,request._request
	2.新的request中多了data属性,request.data客户端提交的请求体中的数据,无论是什么编码都在request.data中
	3.其他的使用和原生的request一模一样
		request.query_params就是原来的request._request.GET
		上传的文件从request.FILES

	
	'''
	1.原生Django提交数据(post),只能处理urlencoded和form-data编码,从request.POST中取
	2.原生Django提交数据(put),处理不了,需要我们从request.body中取出来进行处理
		分不同编码格式:
			urlencoded------》例:name=lqz&age=19---》使用字符串切割的方式.split
			json----->{'xxx':'xx','yyy':'yy'}--->需要自己进行json.loads反序列化
	3.原生Django不能处理json提交的数据(post/put),需要自己做反序列化
		json----->{'xxx':'xx','yyy':'yy'}--->需要自己进行json.loads反序列化
	4.新的request解决了所有问题:request.data
	'''

二、DRF之序列化组件

	序列化类(组件)可以做的事情:
		1.序列化,QuerySet对象,单个对象做序列化给前端
		2.反序列化数据校验:前端传入数据后校验数据是否合法
		3.反序列化数据保存:前端传入数据,存到数据库中

序列化介绍

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

序列化类的作用:做序列化、反序列化、反序列化校验

序列化步骤

	1.写一个py文件,叫serializer.py(命名随意)
	2.写一个序列化类,继承serializers.Serializer,
	3.在类中编写需要序列化的字段
		例:name=serializers.CharField()
	4.在视图类中使用,导入models文件中的类books,然后实例化得到对象,对查出来的query对象们,
		对单个对象序列化并传入instance=books参数
		如果query是复数,一定要串many=True,如果query是单个对象,就无需传入many
	5.序列化类对象:ser.data---->字典或列表----->通过Response将json格式字符串返回给前端

序列化组件的基本使用

1.创建一个py文件 ----》serializer.py
	from rest_framework import serializers
	
	class BookSerializer(serializers.Serializer):
	    name = serializers.CharField(max_length=18, min_length=2, required=True)
	    price = serializers.IntegerField(required=True)
	    

2.view.py文件中
	from app01 import models
	from rest_framework.views import APIView
	from rest_framework.response import Response
	from .serializer import BookSerializer
	
	class BookView(APIView):
	    def get(self, request):
	        book_list = models.Book.objects.all()
	        ser = BookSerializer(instance=book_list, many=True)  # 序列化多条需要many=True
	        return Response({'code': 100, 'msg': '查询成功', 'results': ser.data}) # 无论是列表还是字典都可以序列化

	class BookDetailView(APIView):
	    def get(self, request, pk):
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        ser = BookSerializer(instance=book_obj)
	        if ser.is_valid():
	            return Response({'code': 100, 'msg': '查询一条成功', 'results': ser})
	        else:
	            return Response(ser.errors)

3.urls.py文件中
	urlpatterns = [
	    path('books/', views.BookView.as_view()),
	    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方法

反序列化的新增

序列化类

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

视图类

	class BookView(APIView):
	    def get(self, request):  # 获取多条数据
	        book_list = models.Book.objects.all()
	        '''instance表示要序列化的数据,many=True表示序列化多条(instance是QuerySet对象)'''
	        ser = BookSerializer(instance=book_list, many=True)  # 序列化多条需要many=True
	        return Response({'code': 100, 'msg': '查询成功', 'results': ser.data})
	
	    def post(self, request):
	        ser = BookSerializer(data=request.data)  # 从前端传递数据从request.data中取出来
	        if ser.is_valid():  # is_valid表示校验前端传入的数据,但是我们没有写校验规则
	            # 保存,需要自己写,要在序列化类BookSerializer中重写create方法
	            ser.save()  # 调用ser.save,自动触发自定义编辑create方法保存数据
	            '''
	            这个时候发送post请求会发生报错,NotImplementedError: `create()` must be implemented.
	            这个时候点击我们点击save查看源码是调用了Save会触发BaseSerializer的方法
			    判断了 如果instance有值执行update,没有值执行create 看到create没有写 所以我们得重写Create
	            '''
	            return Response({'code': 100, 'msg': '添加成功', 'results': ser.data})
	        else:
	            return Response({'code': 101, 'msg': ser.errors})

反序列化的新增

序列化类

	class BookSerializer(serializers.Serializer):
	    name = serializers.CharField()
	    price = serializers.IntegerField()
	
	    # 修改对象
	    def update(self, instance, validated_data):
	        # instance 要修改的对象
	        # validated_date 校验过后的数据
	        instance.name = validated_data.get('name')
	        instance.price = validated_data.get('price')
	        instance.save()  # orm的单个对象,修改了单个对象的属性,只要调用对象.save就可以修改保存到数据库
	        return instance  # 记得把修改的对象返回

视图类

	class BookDetailView(APIView):
	    def get(self, request, pk):  # 获取单条数据
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        ser = BookSerializer(instance=book_obj)
	        return Response({'code': 100, 'msg': '查询一条成功', 'results': ser.data})
	
	    def put(self, request, pk):
	        book_obj = models.Book.objects.filter(pk=pk).first()
	        ser = BookSerializer(instance=book_obj,data=request.data)
	        if ser.is_valid():
	            ser.save()  # 同新增一样,需要重写update方法
	            return Response({'code': 100, 'msg': '修改一条成功', 'results': ser.data})
	        else:
	            return Response({'code': 101, 'msg': ser.errors})

删除单条

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

反序列化的校验

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

  • 局部钩子
  • 全局钩子

代码实现

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

class BookSerializer(serializers.Serializer):
    name = serializers.CharField(max_length=18, min_length=2, required=True)
    price = serializers.IntegerField(required=True)
    publish = serializers.CharField(min_length=3)

    # 新增一条数据
    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_date 校验过后的数据
        instance.name = validated_data.get('name')
        instance.price = validated_data.get('price')
        instance.save()  # orm的单个对象,修改了单个对象的属性,只要调用对象.save就可以修改保存到数据库
        return instance  # 记得把修改的对象返回

    # 局部钩子
    def validate_price(self,price):
        if price < 10 or price > 999:
            raise ValidationError('价格不能高于999或者低于10')
        return price

    # 全局钩子
    def validate(self, attrs):
        # 校验过后的数据,出版社后三位文字与书名后三位不能一样
        if attrs.get('publish')[-3] == attrs.get('name')[-3]:
            raise ValidationError('出版社后三位文字不能与书名后三位一样!')
        return attrs

序列化器常用字段和字段参数

常用字段类

字段名字段构造方式
CharFieldCharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)
IntegerFieldIntegerField(max_value=None, min_value=None)
FloatFieldFloatField(max_value=None, min_value=None)
BooleanFieldBooleanField()
NullBooleanFieldNullBooleanField()
FloatFieldmax_value=None, min_value=None
DecimalFieldDecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置
TimeFieldTimeField(format=api_settings.TIME_FORMAT, input_formats=None)
DateFieldDateField(format=api_settings.DATE_FORMAT, input_formats=None)
DateTimeFieldDateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None)
EmailFieldEmailField(max_length=None, min_length=None, allow_blank=False)
RegexFieldRegexField(regex, max_length=None, min_length=None, allow_blank=False)
SlugFieldSlugField(maxlength=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9-]+
URLFieldURLField(max_length=200, min_length=None, allow_blank=False)
UUIDFieldUUIDField(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"
IPAddressFieldIPAddressField(protocol=’both’, unpack_ipv4=False, **options)
DurationFieldDurationField()
ChoiceFieldChoiceField(choices) choices与Django的用法相同
MultipleChoiceFieldMultipleChoiceField(choices)
FileFieldFileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ImageFieldImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)
ListFieldListField(child=, min_length=None, max_length=None)
DictFieldDictField(child=)

在序列化器内使用这些字段则是将接收到的字段转换成某个类型的。
如:id字段值为int类型,我们通过id=serializer.CharField()来接收,那么返回到视图里面的则是字符串类型了。

常用字段参数

选项参数:字段不同可使用的参数也不同

参数名称作用
max_length最大长度
min_lenght最小长度
allow_blank是否允许为空
trim_whitespace是否截断空白字符

IntegerField

参数名称作用
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页面时,显示的字段帮助提示信息

序列化器-source的使用

提前准备好models.py中的数据

	from django.db import models

	# 创建关联表
	class Book(models.Model):
	    name = models.CharField(max_length=32)
    	price = models.DecimalField(max_digits=5, decimal_places=2)
	
	    # 外键 书与出版社 -对多,关联字段写在多的一方,写在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)
	    city= models.CharField(max_length=32)
		email = models.EmailField()
		
	class Author(models.Model):
	    name = models.CharField(max_length=32)
	    age = models.IntegerField()

source

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

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

	'serializer.py'
	class BookSerializer(serializers.Serializer):
	
		'自有字段,修改字段,映射字段,直接随意写序列化字段名'
		book_price = serializers.CharField(source='price')
	    name = serializers.CharField(source='name')

		'关联字段,通过外键获取'
		一对一、一对多的关联,直接使用外键字段点
			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(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)>]>"
	    """

在这里插入图片描述

定制字段的两种方式

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

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

SerializerMethodField定制
返回给前端的格式

	 {
        "name": "西游记",
        "price": "66.00",
        "publish_detail": {
            "name": "东方出版社",
            "city": "东方"
        },
        "author_list": [
            {
                "name": "张三",
                "city": 19
            }
        ]
    },

高级序列化之SerializerMethodField

	class BookSerializer(serializers.Serializer):
	    name = serializers.CharField()
	    price = serializers.CharField()
	
	    # 定制返回格式----serializerMethodField
	    # 只要写了这个字段类serializerMethodField,必须配合get_字段名
	    publish_detail = serializers.SerializerMethodField()
	    def get_publish_detail(self, obj):
	        print(obj) # 当前序列化到的book对象
	        return {'name': obj.publish.name, 'city': obj.publish.city}
	
	
	    author_list = serializers.SerializerMethodField()
	    def get_author_list(self,obj):
	        l = []
	        for author in obj.authors.all():
	            l.append({'name': author.name, 'city': author.age})
	        return l

在这里插入图片描述


在表模型中定制
返回给前端格式

	{
        "name": "西游记",
        "price": "66.00",
        "publish_detail": {
            "name": "东方出版社",
            "city": "东方"
        },
        "author_list": [
            {
                "name": "张三",
                "age": 19
            }
        ]
    },
	'models.py'
	class Book(models.Model):
	    name = models.CharField(max_length=32)
	    price = models.DecimalField(max_digits=5, decimal_places=2)
	    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
	    authors = models.ManyToManyField(to='Author')
		
		# 定制在模型类中的方法
	    def publish_detail(self):
	        return {'name': self.publish.name, 'city': self.publish.city}
	
	    def author_list(self):
	        l = []
	        for author in self.authors.all():
	            l.append({'name': author.name, 'age': author.age})
	        return l
	
	''
	class BookSerializer(serializers.Serializer):
	    name = serializers.CharField()
	    price = serializers.CharField()
	
	    # 定制返回格式----在表模型中写方法,在序列化类中做映射
	    publish_detail = serializers.DictField()
	    author_list = serializers.ListField()

在这里插入图片描述
总结:

  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.全局钩子校验规则

新增

views.py

	class BookView(APIView):
	    def get(self, request):
	        books = models.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(ser.data)
	        else:
	            return Response(ser.errors)

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

  • read_only:表明该字段仅用于序列化输出,默认是False,序列化过程
  • write_only:表明该字段经用于反序列化输入,默认是False,反序列化过程

serializer.py

	class BookSerializer(serializers.Serializer):
	    '''公共的'''
	    name = serializers.CharField()
	    price = serializers.CharField()
	
	    '''只用来做反序列化'''
	    # publish = serializers.IntegerField(write_only=True)
	    publish_id = serializers.IntegerField(write_only=True)
	    authors = serializers.ListField(write_only=True)
	
	    '''只用来做序列化'''
	    publish_detail = serializers.SerializerMethodField(read_only=True)
	    author_list = serializers.SerializerMethodField(read_only=True)
	    def get_publish_detail(self, obj):
	        print(obj) # 当前序列化到的book对象
	        return {'name': obj.publish.name, 'city': obj.publish.city}
	
	    def get_author_list(self,obj):
	        l = []
	        for author in obj.authors.all():
	            l.append({'name': author.name, 'city': author.age})
	        return l
	
	    '''新增,继承Serializer类必须要重写create方法'''
	    def create(self, validated_date):  # validated_date是校验过后的数据
	        authors = validated_date.pop('authors')
	        print(authors)
	        print('------')
	        print(validated_date)
	        '''
	        这里不能用**validated_date打散的方式,因为我们写的是 publish=1这种的它需要放一个对象,
	        而其实存进去是publish_id=1,一个数字,所以无法使用,
	        如果想要使用这个**打散的方式,需要把上面的publish改成publish_id,然后前端发送的也改成publish_id
	        '''
	        # name = validated_date.get('name')
	        # price = validated_date.get('price')
	        # publish_id = validated_date.get('publish')
	        # book = models.Book.objects.create(name=name,price=price,publish_id=publish_id)
	        book = models.Book.objects.create(**validated_date)
	        book.authors.add(*authors)
	        return book

在这里插入图片描述


修改

修改跟新增不一样,因为得知道修改那一条,所以接口就得不一样,需要获取到想要修改的一条数据

urls.py

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

views.py

	class BookDetailView(APIView):
	    def put(self, request, pk):
	        book = models.Book.objects.get(id=pk)
	        ser = BookSerializer(instance=book, data=request.data)
	        if ser.is_valid():
	            ser.save()
	            return Response(ser.data)
	        else:
	            return Response(ser.errors)

serializer.py

	class BookSerializer(serializers.Serializer):
	    '''公共的'''
	    name = serializers.CharField()
	    price = serializers.CharField()
	
	    '''只用来做反序列化'''
	    # publish = serializers.IntegerField(write_only=True)
	    publish_id = serializers.IntegerField(write_only=True)
	    authors = serializers.ListField(write_only=True)
	
	    '''只用来做序列化'''
	    publish_detail = serializers.SerializerMethodField(read_only=True)
	    author_list = serializers.SerializerMethodField(read_only=True)
	    def get_publish_detail(self, obj):
	        print(obj) # 当前序列化到的book对象
	        return {'name': obj.publish.name, 'city': obj.publish.city}
	
	    def get_author_list(self,obj):
	        l = []
	        for author in obj.authors.all():
	            l.append({'name': author.name, 'city': author.age})
	        return l
	        
		
		'''修改,继承Serializer类必须要重写update方法'''
	    def update(self, instance,validated_date):
	    	# validated_data 校验过后的数据
	        authors = validated_date.pop('authors')
	        '''如果是用的publish的,又想要一次性传入进去,就得自己在定义一个publish_id'''
	        # validated_date['publish_id'] = validated_date.pop('publish')
	        for key in validated_date:
	            setattr(instance, key, validated_date[key])
	
	        instance.save()
	        '''也可以直接使用set修改,无需清空后再添加'''
	        instance.authors.clear()
	        instance.authors.add(*authors)
	        # instance.authors.set(authors)  # 无需打散传入了
	        return instance

在这里插入图片描述


反序列化之数据校验

serializer.py
这里沿用上面案例的数据添加数据校验,数据校验和上面的反序列校验一样,所以这里就不细说了

	'校验自有的字段'
	name = serializers.CharField(max_length=8,error_messages={'max_length:不能超过8位'})
	
	'''局部钩子校验'''
    def validate_name(self,name):
        l = ['sb', '傻逼','fw']
        for i in l:
            if i in name:
                raise ValidationError('图书命名中不能有敏感词!')
        return name
        
	'''局部钩子校验'''
    def validate_price(self, price):
        if int(price) < 10 or int(price) > 999:
            raise ValidationError('图书价格不能小于2位数或者不能大于4位数!')
        return price


    '''全局钩子校验'''
    def validate(self,attrs):
        print(attrs)
        publish_id = attrs.get('publish_id')
        print(publish_id)
        publish_obj = models.Publish.objects.filter(pk=publish_id).first()
        print(publish_obj.name)
        if attrs.get('name')[-2] == publish_obj.name[-2]:
            raise ValidationError('图书名后两位不能与出版社后两位相同')
        return attrs

在这里插入图片描述


ModelSerializer的使用

ModelSerializer继承了Serializer,帮我们完成了很多的操作,比如跟表模型强关联,大部分的请求postput等,几乎不用再序列化的时候重写createupdate方法了。

使用方式跟上面的Serializer的基本一样,我这里就还是用上面的案例来操作

serializer.py

	'''继承ModelSerializer类做序列化、反序列化以及校验操作'''
class BookModelSerializer(serializers.ModelSerializer):
    '''其他跟Serializer一样使用,不过无需再重写create和update方法'''
    '''只用来做序列化,这些扩写的字段也需要在field中注册'''
    publish_detail = serializers.SerializerMethodField(read_only=True)
    author_list = serializers.SerializerMethodField(read_only=True)

    def get_publish_detail(self, obj):
        print(obj)  # 当前序列化到的book对象
        return {'name': obj.publish.name, 'city': obj.publish.city}

    def get_author_list(self, obj):
        l = []
        for author in obj.authors.all():
            l.append({'name': author.name, 'city': author.age})
        return l

    class Meta:
        model = models.Book  # 这两句是会把表模型中的Book,所有字段映射过来
        # fields = '__all__'
        fields = ['name', 'price', 'publish', 'authors', 'publish_detail', 'author_list']
        '''如果使用表模型定制字段,然后在fields中注册了,可以直接在extra_kwargs中设置字段属性read_only'''
        extra_kwargs = {  # 给某个或某几个字段设置字段属性
            'name': {'max_length': 18, 'min_length': 2},
            'price': {'max_digits': 8, 'decimal_places': 3},
            'publish': {'write_only': True},
            'authors': {'write_only': True},
            # 'publish_detail': {'read_only': True},
            # 'author_list': {'read_only': True},
        }
    '''局部钩子'''
    def validate_name(self,name):
        l = ['sb', '傻逼','fw']
        for i in l:
            if i in name:
                raise ValidationError('图书命名中不能有敏感词!')
        return name
        
    def validate_price(self, price):
        if int(price) < 10 or int(price) > 999:
            raise ValidationError('图书价格不能小于2位数或者不能大于4位数!')
        return price
        
    # '''全局钩子'''
    # def validate(self,attrs):
    #     print(attrs)
    #     publish_id = attrs.get('publish')
    #     print(publish_id)
    #     publish_obj = models.Publish.objects.filter(pk=publish_id).first()
    #     print(publish_obj.name)
    #     if attrs.get('name')[-2] == publish_obj.name[-2]:
    #         raise ValidationError('图书名后两位不能与出版社后两位相同')
    #     return attrs

这里为了区分一下,我就更改了接口路由

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

views.py,视图类也是为了做一下区分,所以我修改了序列化类名,其他跟serializer那个操作一样

	'''继承serializer类的ModelSerializer方式'''
	class BookModelView(APIView):
	    def get(self,request):
	        book = models.Book.objects.all()
	        ser = BookModelSerializer(instance=book,many=True)
	        return Response(ser.data)
	
	    def post(self,request):
	        ser = BookModelSerializer(data=request.data)
	        if ser.is_valid():
	            ser.save()
	            return Response(ser.data)
	        else:
	            return Response(ser.errors)
	
	
	class BookDetailModelView(APIView):
	    def put(self, request, pk):
	        book = models.Book.objects.filter(pk=pk).first()
	        ser = BookModelSerializer(instance=book, data=request.data)
	        if ser.is_valid():
	            ser.save()
	            return Response(ser.data)
	        else:
	            return Response(ser.errors)

使用方法

	1.定义一个类继承ModelSerializer
	2.类内部写内部类 Class Meta:(注意区分大小写)
	3.在内部类中指定model(需要序列化的表)
	4.在内部类中指定fields(要序列化的字段,写__all__表示所有,不包含方法,也可以写一个个的字段)
	5.在内部类中指定extra_kwargs,给字段天津爱字段参数
	6.在序列化类中,可以重写某个字段,优先使用你重写的
		name = serializers.SerializerMethodField()
		def get_name(self,obj):
			return obj.name + '__fw'
	7.以后几乎不需要重写create和update方法了(除非有需求要用到就得重写)
		-ModelSerializer写好了,兼容性更好,任意表都可以直接存

总结

	ModelSerializer继承自Serializer,我们可以直接查询多条,单条,新增和修改等操作
	针对fields=['name','email','city']里面的参数要求:
		1.只要是序列化的字段和反序列化的字段,都要在这注册 如:publish_detail,publish,authors
		2.序列化的字段,可能不是表模型的字段,是自己写的方法
		3.序列化的字段,可能是使用SerializerMethodField,也要注册

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/263371.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

老师的责任和义务

作为一名老师&#xff0c;我们的责任和义务是重大的。在教育领域&#xff0c;我们扮演着至关重要的角色&#xff0c;肩负着培养下一代人才的重任。下面&#xff0c;我将以知乎的口吻&#xff0c;从几个方面谈谈老师的责任和义务。 确保学生获得高质量的教育。这包括制定合理的教…

【XR806开发板试用】通过http请求从心知天气网获取天气预报信息

1. 开发环境搭建 本次评测开发环境搭建在windows11的WSL2的Ubuntu20.04中&#xff0c;关于windows安装WSL2可以参考文章: Windows下安装Linux(Ubuntu20.04)子系统&#xff08;WSL&#xff09; (1) 在WSL的Ubuntu20.04下安装必要的工具的. 安装git: sudo apt-get install git …

在线客服系统推荐:优质选择助您提升客户服务体验

大部分企业依靠在线客服系统和客户达成联系&#xff0c;他为客户和企业之间建立了有效的沟通桥梁。市场上这么多的在线客服系统哪个好呢&#xff1f; 1、明确自己的需求。 这一点是最重要的&#xff0c;要先明确公司使用客服系统是想做售前咨询还是售后服务。不同的需求相对应…

[笔记]ByteBuffer垃圾回收

参考&#xff1a;https://blog.csdn.net/lom9357bye/article/details/133702169 public static void main(String[] args) throws Throwable {List<Object> list new ArrayList<>();Thread thread new Thread(() -> {ByteBuffer byteBuffer ByteBuffer.alloc…

自动评估作业,支持订正最终得分、查看关联代码|ModelWhale 版本更新

冬至时节&#xff0c;2023 已进入尾声&#xff0c;ModelWhale 于今日迎来新一轮的版本更新&#xff0c;与大家一起静候新年的到来。 本次更新中&#xff0c;ModelWhale 主要进行了以下功能迭代&#xff1a; 自动评估作业 新增 提交代码&#xff08;团队版✓ &#xff09;新增…

【Netty】编解码器

目录 Java的编解码Netty编解码器概念解码器(Decoder)编码器(Encoder)编码解码器Codec Java的编解码 编码&#xff08;Encode&#xff09;称为序列化&#xff0c; 它将对象序列化为字节数组&#xff0c;用于网络传输、数据持久化或者其它用途。 解码&#xff08;Decode&#x…

本地无法连接注册中心eureka,如何进行feign调用,并快速启动本地调试?

前言 今年进入了个新公司&#xff0c;接手代码时遇到了比较蛋疼的事情&#xff0c;本地代码调试遇到了层层阻碍&#xff0c;无法一键简单启动就算了&#xff0c;我就忍了&#xff0c;一番操作终于启动起了了&#xff0c;启动又慢&#xff0c;启动了2~3分钟&#xff0c;后面调本…

【音视频】remb twcc原理

目录 twcc简介 WebRTC REMB 参考文档 twcc简介 TWCC全称是Transport wide Congestion Control&#xff0c;是webrtc的最新的拥塞控制算法。其原理是在接收端保存数据包状态&#xff0c;然后构造RTCP包反馈给发送端&#xff0c;反馈信息包括包到达时间、丢包状态等&#xff…

养老院自助饮水机(字符设备驱动)

目录 1、项目背景 2、驱动程序 2.1 三层架构 2.2 驱动三要素 2.3 字符设备驱动 2.3.1 驱动模块 2.3.2 应用层 3、设计实现 3.1 项目设计 3.2 项目实现 3.2.1 驱动模块代码 3.2.2 用户层代码 4、功能特性 5、技术分析 6. 总结与未来展望 1、项目背景 养老院的老人…

社交网络分析(汇总)

这里写自定义目录标题 写在最前面社交网络分析系列文章汇总目录 提纲问题一、社交网络相关定义和概念提纲问题1. 社交网络、社交网络分析&#xff1b;2. 六度分隔理论、贝肯数、顿巴数&#xff1b;3. 网络中的数学方法&#xff1a;马尔科夫过程和马尔科夫链、平均场理论、自组织…

仿悬赏猫任务平台源码 悬赏任务系统源码 带支付接口

源码介绍 最新仿悬赏猫任务平台源码 悬赏任务系统源码 带支付接口&#xff0c; 全新开发悬赏任务系统&#xff0c;功能齐全&#xff0c;包含接任务&#xff0c;发布任务&#xff0c; 店铺关注&#xff0c;置顶推荐&#xff0c;排行榜&#xff0c;红包大厅&#xff0c;红包抽奖…

Android Studio如何实现 成语接龙游戏(简单易上手)

该项目是一个基于Android Studio和Java语言编写的成语接龙游戏App。成语接龙是一种经典的中文文字游戏&#xff0c;旨在测试玩家的词汇量和思维敏捷性。该成语接龙游戏App旨在提供一种有趣、挑战性和教育性的游戏体验。玩家可以通过游戏提高自己的中文词汇量和思维敏捷性&#…

Text Intelligence - TextIn.com AI时代下的智能文档识别、处理、转换

本指南将介绍Text Intelligence&#xff0c;AI时代下的智能文档技术平台 Textin.com 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#xff0c;阿里云认…

KubeSphere应用【六】中间件部署

一、Mysql部署 1.1创建配置字典 [client] default-character-setutf8mb4 [mysql] default-character-setutf8mb4[mysqld] sql_modeSTRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION init_connectSET…

【分治算法】之汉诺塔问题

汉诺塔问题 三根柱子 把A柱子上的盘子全部挪到C上&#xff0c;且每次挪动的时候 小的必须在大的上面 分治算法的思想; 分&#xff1a;把一个大问题拆成若干个小的子问题&#xff0c;每个子问题相互独立&#xff1b; 治&#xff1a;求解每个子问题的&#xff08;递归&#xf…

前端FLV视频直播解决方案

项目背景&#xff1a; 1. 后台给出一个地址&#xff0c;持续不断的推送flv视频流。 2.前端需要接收视频流&#xff0c;并寻找合适的播放插件。 一开始&#xff1a; 其实用的是xgplayer&#xff08;西瓜视频&#xff09;。 官网地址&#xff1a;西瓜播放器 使用的是直播&a…

开放式耳机怎么选?2023高人气品牌推荐:新手避坑必看!

自从开放式耳机风靡市场以来&#xff0c;大家对于开放式耳机的选购也越发摸不着头脑。价格从百元到千元不等&#xff0c;就连大品牌的产品口碑也褒贬不一。 不少人私信向我询问&#xff1a; 1、难道只有千元价位的开放式耳机才好吗&#xff1f;2、是否有价格更实惠且性价比更…

如何使用 Helm 在 K8s 上集成 Prometheus 和 Grafana|Part 1

本系列将分成三个部分&#xff0c;您将学习如何使用 Helm 在 Kubernetes 上集成 Prometheus 和 Grafana&#xff0c;以及如何在 Grafana 上创建一个简单的控制面板。Prometheus 和 Grafana 是 Kubernetes 最受欢迎的两种开源监控工具。学习如何使用 Helm 集成这两个工具&#x…

C#电源串口调试

目的 记录串口调试的遇到的一些问题以及相应的解决方法 1.串口定义:串口是计算机与其他硬件传输数据的通道&#xff0c;在计算机与外设通信时起到重要作用 2.串口通信的基础知识 C#中的串口通信类 C#使用串口通信类是SerialPort(),该类使用方法是 new 一个 SerialPort对象 为S…

Prometheus-JVM

一. JVM监控 通过 jmx_exporter 启动端口来实现JVM的监控 Github Kubernetes Deployment Java 服务&#xff0c;修改 wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.19.0/jmx_prometheus_javaagent-0.19.0.jar# 编写配置文件&#xff0…