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

news2024/11/24 12:26:10

文章目录

  • 一、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,也要注册

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

109基于MATLAB 中的设施布局设计和位置分配

基于MATLAB 中的设施布局设计和位置分配&#xff0c;通过PSO算法进行最佳位置匹配。程序已调通&#xff0c;可直接运行。 109设施布局设计和位置分配通 (xiaohongshu.com)

2023光伏“洗牌”,鼎捷数智方案如何助力企业抓住时代契机?

2023年的光伏行业正进入重新“洗牌”的阶段&#xff01; 一方面&#xff0c;在“双碳政策”和全球能源危机的双重驱动下&#xff0c;我国光伏全产业链迎来高速增长&#xff0c;成为外贸出口“新三样”之一。光伏发电正加速取代传统化石能源&#xff0c;在电网中发挥着日益重要的…

vue3+Ts

安装 命令含义可参考typescript文章中的自动编译部分 npm create vitelatest vuets-project -- --template vue-tsvs code插件 Vue Language Features (Volar)对.vue文件进行实时的类型错误反馈TypeScript Vue Plugin (Volar) 用于支持在TS中import*.vue文件&#xff08;mai…

【2023年网络安全优秀创新成果大赛专刊】银行数据安全解决方案(天空卫士)

在2023年网络安全优秀创新成果大赛&#xff0c;成都分站中&#xff0c;天空卫士银行数据安全方案获得优秀解决方案奖。与此同时&#xff0c;天空卫士受信息安全杂志邀请&#xff0c;编写《银行数据安全解决方案》。12月6日&#xff0c;天空卫士编写的《银行数据安全解决方案》做…

Debezium发布历史20

原文地址&#xff1a; https://debezium.io/blog/2017/09/25/streaming-to-another-database/ 欢迎关注留言&#xff0c;我是收集整理小能手&#xff0c;工具翻译&#xff0c;仅供参考&#xff0c;笔芯笔芯. 将数据流式传输到下游数据库 九月 25, 2017 作者&#xff1a; Jiri…

期货交易策略模拟测试-基于CLBISO01策略-2023.12.22

采取与昨天同样的策略进行盘中模拟测试&#xff0c;今天行情还可以&#xff0c;挺“顺溜”。

1. 结构型模式 - 适配器模式

亦称&#xff1a; 封装器模式、Wrapper、Adapter 意图 适配器模式是一种结构型设计模式&#xff0c; 它能使接口不兼容的对象能够相互合作 问题 假如你正在开发一款股票市场监测程序&#xff0c; 它会从不同来源下载 XML 格式的股票数据&#xff0c; 然后向用户呈现出美观的图…

电气 接近开关

npn&#xff1a;和负载&#xff08;控制器或者继电器&#xff09;共阳极&#xff0c;低电平响应 pnp&#xff1a;和负载共阴极&#xff0c;高电平响应

声音克隆定制丰富和的系统源码+完整的代码包+搭建教程

随着科技的进步&#xff0c;人工智能&#xff08;AI&#xff09;技术已经逐渐渗透到我们生活的各个领域。声音克隆技术&#xff0c;作为AI领域的一个重要分支&#xff0c;通过模仿人类的声音特征&#xff0c;生成与目标声音相似的语音。这项技术在语音合成、语音识别、虚拟现实…

vue2 之 实现pdf电子签章

一、前情提要 1. 需求 仿照e签宝&#xff0c;实现pdf电子签章 > 拿到pdf链接&#xff0c;移动章的位置&#xff0c;获取章的坐标 技术 : 使用fabric pdfjs-dist vuedraggable 2. 借鉴 一位大佬的代码仓亏 : 地址 一位大佬写的文章 &#xff1a;地址 3. 优化 在大佬的代码…

椰油酰胺,预计到2026年将达到5.25亿美元

椰油酰胺&#xff0c;也称为椰油酰胺 DEA 或椰油酰胺 MEA&#xff0c;是从椰子油中提取的脂肪酸酰胺的混合物。它通常用作洗发水、香皂和化妆品等个人护理产品中的乳化剂和发泡剂。近年来&#xff0c;受个人护理产品需求增加以及椰油酰胺在食品和制药等其他行业的广泛使用推动&…

安全、高效的MySQL DDL解决方案

MySQL作为目前应用最广泛的开源关系型数据库&#xff0c;是许多网站、应用和商业产品的主要数据存储。在生产环境&#xff0c;线上数据库常常面临着持续的、不断变化的表结构修改&#xff08;DDL&#xff09;&#xff0c;如增加、更改、删除字段和索引等等。其中一些DDL操作在M…

微服务 Spring Cloud 10,如何追踪微服务调用?服务治理的常见手段

目录 一、服务追踪的作用1、优化系统瓶颈2、优化链路调用3、故障排查4、性能优化5、生成网络拓扑图4、透明传输数据 二、节点管理1、服务调用失败一般有两类原因造成&#xff1a;2、服务调用失败的解决方式&#xff1a;3、服务调用失败的具体解决方式&#xff1a; 三、负载均衡…

电脑怎么重装系统?跟着步骤轻松搞定!

电脑系统随着时间的推移可能会变得迟缓或出现其他问题&#xff0c;而重装系统是解决这些问题的有效方法之一。本文将介绍三种电脑怎么重装系统的方法&#xff0c;帮助您在不同情况下选择适合自己的方案&#xff0c;让电脑焕然一新。 方法1&#xff1a;使用系统自带的恢复选项 …

[SWPUCTF 2021 新生赛]gift_F12

打开环境 题目有提示&#xff08;F12&#xff09;&#xff0c;那就查看一下源代码 直接滑到最后 看提示猜测&#xff0c;flag就在源代码里了 ctrlf查找flag 最后得到flag&#xff0c;改一下形式就可以了

ELFK日志收集

文章目录 第一章:ELK日志收集系统介绍日志收集重要性ELK介绍EFK介绍ELFK介绍ES部署Kibana部署第二章:Logstach日志收集Logstash介绍Logstash安装Logstash Input输入插件Logstash Filter过滤插件Logstash Output输出插件Input fileFilter mutatesplit示例add_field示例remove_…

Flink系列之:Savepoints

Flink系列之&#xff1a;Savepoints 一、Savepoints二、分配算子ID三、Savepoint 状态四、算子五、触发Savepoint六、Savepoint 格式七、触发 Savepoint八、使用 YARN 触发 Savepoint九、使用 Savepoint 停止作业十、从 Savepoint 恢复十一、跳过无法映射的状态恢复十二、Resto…

阿里云大模型数据存储解决方案,为 AI 创新提供推动力

云布道师 随着国内首批大模型产品获批名单问世&#xff0c;百“模”大战悄然开启。在这场百“模”大战中&#xff0c;每一款大模型产品的诞生&#xff0c;都离不开数据的支撑。如何有效存储、管理和处理海量多模态数据集&#xff0c;并提升模型训练、推理的效率&#xff0c;保…

【湖仓一体尝试】MYSQL和HIVE数据联合查询

爬了两天大大小小的一堆坑&#xff0c;今天把一个简单的单机环境的流程走通了&#xff0c;记录一笔。 先来个完工环境照&#xff1a; mysqlhadoophiveflinkicebergtrino 得益于IBM OPENJ9的优化&#xff0c;完全启动后的内存占用&#xff1a; 1&#xff09;执行联合查询后的…