【2020Python修炼记】Django Rest Framework-2-drf序列化组件

【目录】

一、什么是序列化器

二、最简单的 serializers.Serializer 序列化器的使用

2.1 定义序列化器

2.2 创建Serializer对象

2.3 序列化器的使用

2.3.1 序列化

2.3.2 反序列化

三、模型序列化器 ModelSerializer 的使用

 

一、什么是序列化器

作用:

1. 序列化,序列化器会把模型对象转换成字典,经过response以后变成json字符串
2. 反序列化,把客户端发送过来的数据,经过request以后变成字典,序列化器可以把字典转成模型
3. 反序列化,完成数据校验功能

二、最简单的 serializers.Serializer 序列化器的使用

准备工作——

=创建子应用 sers

python manage.py startapp sers

=准备模型表(记得操作-数据库迁移,以及新增基础数据)

from django.db import models

# Create your models here.

class Student(models.Model):
    # 模型字段
    name = models.CharField(max_length=100,verbose_name="姓名",help_text="提示文本:账号不能为空!")
    sex = models.BooleanField(default=True,verbose_name="性别")
    age = models.IntegerField(verbose_name="年龄")
    class_null = models.CharField(max_length=5,verbose_name="班级编号")
    description = models.TextField(verbose_name="个性签名")

    class Meta:
        db_table="tb_student"
        verbose_name = "学生"
        verbose_name_plural = verbose_name
Student

 

2.1 定义序列化器

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

注意:serializer不是只能为数据库模型类定义,也可以为非数据库模型类的数据定义。serializer是独立于数据库之外的存在。

from rest_framework import serializers

# 声明序列化器,所有的序列化器都要直接或者间接继承于 Serializer
# 其中,ModelSerializer是Serializer的子类,ModelSerializer在Serializer的基础上进行了代码简化
class StudentSerializer(serializers.Serializer):
    """学生信息序列化器"""
    # 1. 需要进行数据转换的字段
    id = serializers.IntegerField()
    name = serializers.CharField()
    age = serializers.IntegerField()
    sex = serializers.BooleanField()
    description = serializers.CharField()

    # 2. 如果序列化器集成的是ModelSerializer,则需要声明调用的模型信息

    # 3. 验证代码

    # 4. 编写添加和更新模型的代码

 

常用字段类型:

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

选项参数:

参数名称作用
max_length 最大长度
min_lenght 最小长度
allow_blank 是否允许为空
trim_whitespace 是否截断空白字符
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页面时,显示的字段帮助提示信息
【总结】

1、字段属性
    title=serializers.CharField(max_length=32,min_length=2,read_only=True)
    price=serializers.DecimalField(max_digits=5, decimal_places=2)
    publish=serializers.CharField(max_length=32,write_only=True)

=1、CharField 必须要有的字段
max_length

=2、DecimalField 必须要有的字段
max_digits
decimal_places

=3、关于 read_only/write_only
===序列化/反序列化
【序列化】 就是将对象转化方便传输和存储字节序列,例如json.dumps就是序列化(狭义的序列化,将字典转化为json字符串),
这样得到的json字符串不仅直接可以在其他语言使用(跨平台比较好),而且可以在前后端进行传输交互(drf序列化器)

【反序列化】 恰恰相反,而是将字节序列转化为对象,json.loads是将json字符串转化为字典,
是狭义的反序列化(因为在python, 一切皆对象,字典是dict( )),
而在项目中,前端传过来的序列化数据通过反序列化得到对象,进一步可以通过ORM操作,存入数据库。

=== read_only/write_only
【read_only】表明该字段仅用于序列化输出,默认 False
read_only 是只用于读,不能写(可以理解为只能从后台到前台,后台操作后直接传前台),
不能对字段进行修改操作(也就是不和数据库进行交互),用在序列化字段里。
例如数据表中,gender只用于传到前端展示。而不用于存到数据库

【write_only】表明该字段仅用于反序列化输入,默认 False
write_only是只写不能读,可以理解为只能前台到后台,
在后台做了逻辑操作后直接存数据库,在反序列化字段使用。
在数据表中,password就是反序列化,不需要传前台,
而re_password是自定义反序列化字段,仅用作后台和密码进行比较,
然后把结果返回前台,所以不存到数据库,在全局钩子使用时要pop掉

【解说版2】
read_only
 该字段仅用于 序列化输出(后端--》前端)
(read_only=True)
即 后端往前端 传送数据时(序列化),该字段只用于显示在前端;
如果从前端往后端输入数据时(反序列化),该字段则可以不传值(这需要提前给该字段增加 null=True 属性,允许字段在反序列化输入时 可以为空,否则数据库会报错)

write_only
该字段仅用于 反序列化输入 (前端--》后端)
(write_only=True)
从前端往后端输入数据时(反序列化),该字段必须传值
后端往前端 传送序列化数据时(序列化),该字段不会显示在前端

用于 反序列化时必填,写入数据库的字段,但是不想后面在前端显示
(一般若不想让字段显示在前端,是直接注释字段;但是这样可能会在反序列化时出问题,因此使用 write_only=True 来控制字段 )

 

2.2 创建Serializer对象

定义好Serializer类后,就可以创建Serializer对象了。

Serializer的构造方法为:

Serializer(instance=None, data=empty, **kwarg)

 

说明:

1)用于序列化时,将模型类对象传入instance参数

2)用于反序列化时,将要被反序列化的数据传入data参数

3)除了instance和data参数外,在构造Serializer对象时,还可通过context参数额外添加数据,如

serializer = AccountSerializer(account, context={'request': request})

 

通过context参数附加的数据,可以通过Serializer对象的context属性获取。

  1. 使用序列化器的时候一定要注意,序列化器声明了以后,不会自动执行,需要我们在视图中进行调用才可以。
  2. 序列化器无法直接接收数据,需要我们在视图中创建序列化器对象时把使用的数据传递过来。
  3. 序列化器的字段声明类似于我们前面使用过的表单系统。
  4. 开发restful api时,序列化器会帮我们把模型数据转换成字典.
  5. drf提供的视图会帮我们把字典转换成json,或者把客户端发送过来的数据转换字典.

 

2.3 序列化器的使用

序列化器的使用分两个阶段:

  1. 在客户端请求时,使用序列化器可以完成对数据的 反序列化(前端-----》后端)。
  2. 在服务器响应时,使用序列化器可以完成对数据的 序列化 (后端-----》前端)。

2.3.1 序列化

2.3.1.1 基本使用

1) 先查询出一个学生对象

from students.models import Student

student = Student.objects.get(id=3)

 

2) 构造序列化器对象

from .serializers import StudentSerializer

serializer = StudentSerializer(instance=student)

 

3)获取序列化数据

通过data属性可以获取序列化后的数据

serializer.data

 

完整视图代码:

from django.views import View
from students.models import Student
from .serializers import StudentSerializer
from django.http.response import JsonResponse
class StudentView(View):
    """使用序列化器序列化转换单个模型数据"""
    def get(self,request,pk):
        # 获取数据
        student = Student.objects.get(pk=pk)
        # 数据转换[序列化过程]
        serializer = StudentSerializer(instance=student)
        print(serializer.data)
        # 响应数据
        return JsonResponse(serializer.data)

 

4)如果要被序列化的是包含多条数据的查询集QuerySet,可以通过添加many=True参数补充说明

"""使用序列化器序列化转换多个模型数据"""
def get(self,request):
    # 获取数据
    student_list = Student.objects.all()

    # 转换数据[序列化过程]
    # 如果转换多个模型对象数据,则需要加上many=True
    serializer = StudentSerializer(instance=student_list,many=True)
    print( serializer.data ) # 序列化器转换后的数据

    # 响应数据给客户端
    # 返回的json数据,如果是列表,则需要声明safe=False  #默认safe=True代表只能序列化字典对象,safe=False代表可以序列化字典以外的对象
   return JsonResponse(serializer.data,safe=False)

 

2.3.1.1 高级使用—— source 和 serializers.SerializerMethodField()的用法

source 用法

source的用法1
—— 修改字段前端显示的名字,真实的字段名是title,想让其在前端显示为name,则可以改写如下:
注意 设定的前端显示名 不能与真实的字段名相同,否则会报错
name=serializers.CharField(max_length=32,min_length=2,read_only=True,source='title')

source的用法2
—— 若表模型中 定义有方法,则可以将方法执行的返回值 传给source
例如
-1- models.py的book模型中,有个test方法
    def test(self):
        # python是强类型语言,不支持字符串和数字直接相加
        return self.title+str(self.price)

-2- 则在序列化器类中的 需要序列化字段中 加上一个字段用于接收方法执行的返回值
    test_result=serializers.CharField(source='test')

source的用法3 ——支持 xx.yy 的跨表操作
例如
-1- models.py --两张一对多关系的publish表模型和 book表模型
book表模型中,加上关联字段
    publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)

publish表模型
class Publish(models.Model):
    name=models.CharField(max_length=32)
    addr=models.CharField(max_length=32)
    def __str__(self):
        return self.name

book表模型的序列化器类-- source='publish.addr'
    publish_addr=serializers.CharField(max_length=32,source='publish.addr')  # objects.name

 

 serializers.SerializerMethodField()的用法

    # =1、需要指定使用SerializerMethodField()的字段
# =2、搭配定义一个方法,规定 get_字段名,方法内写返回给字段的数据
### 取出图书的出版社详细信息(id,name,addr)
class BookSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False)
    name = serializers.CharField(max_length=32,min_length=2,source='title')
    price = serializers.DecimalField(max_digits=5, decimal_places=2)
# =1、需要指定使用SerializerMethodField()的字段 publish = serializers.SerializerMethodField() # =2、搭配定义一个方法,规定 get_字段名,方法内写返回给字段的数据 def get_publish(self,obj): dic={'name':obj.publish.name,'addr':obj.publish.addr} return dic # 这里返回什么,就序列化显示什么

 

 

2.3.2 反序列化

=1  数据验证

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

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

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

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

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

如我们前面定义过的BookInfoSerializer

# models.py
class
BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" id = serializers.IntegerField(label='ID', read_only=True) btitle = serializers.CharField(label='名称', max_length=20) bpub_date = serializers.DateField(label='发布日期', required=False) bread = serializers.IntegerField(label='阅读量', required=False) bcomment = serializers.IntegerField(label='评论量', required=False) image = serializers.ImageField(label='图片', required=False)

 

通过构造序列化器对象,并将要反序列化的数据传递给data构造参数,进而进行验证

# views.py

from booktest.serializers import BookInfoSerializer

data = {'bpub_date': 123}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # 返回False
serializer.errors
# {'btitle': [ErrorDetail(string='This field is required.', code='required')], 
'bpub_date': [ErrorDetail(string='Date has wrong format. Use one of these formats instead: YYYY[-MM[-DD]].', code='invalid')]}
serializer.validated_data # {} data = {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # True serializer.errors # {} serializer.validated_data # OrderedDict([('btitle', 'python')])

 

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

REST framework接收到此异常,会向前端返回HTTP 400 Bad Request响应。

# Return a 400 response if the data was invalid.
serializer.is_valid(raise_exception=True)

 

 =2 局部钩子/全局钩子/ 自定义验证器

如果觉得这些还不够,需要再补充定义验证行为,可以使用以下三种方法:

1) 局部钩子 ——  validate_字段名

<field_name>字段进行验证,如

# serializer.py
class
BookInfoSerializer(serializers.Serializer): """图书数据序列化器""" ... def validate_btitle(self, value): if 'django' not in value.lower(): raise serializers.ValidationError("图书不是关于Django的") return value

测试 views.py

from booktest.serializers import BookInfoSerializer
data = {'btitle': 'python'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # False   
serializer.errors
#  {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}

 

2) 全局钩子—— validate

在序列化器中需要同时对多个字段进行比较验证时,可以定义validate方法来验证,如

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    ...

    def validate(self, attrs):
        bread = attrs['bread']
        bcomment = attrs['bcomment']
        if bread < bcomment:
            raise serializers.ValidationError('阅读量小于评论量')
        return attrs

测试

from booktest.serializers import BookInfoSerializer
data = {'btitle': 'about django', 'bread': 10, 'bcomment': 20}
s = BookInfoSerializer(data=data)
s.is_valid()  # False
s.errors
#  {'non_field_errors': [ErrorDetail(string='阅读量小于评论量', code='invalid')]}

 

3) 自定义验证器方法—— validators 

在字段中添加validators选项参数,也可以补充验证行为,如

# serializer.py

def about_django(value):
    if 'django' not in value.lower():
        raise serializers.ValidationError("图书不是关于Django的")

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    id = serializers.IntegerField(label='ID', read_only=True)
    btitle = serializers.CharField(label='名称', max_length=20, validators=[about_django])
    bpub_date = serializers.DateField(label='发布日期', required=False)
    bread = serializers.IntegerField(label='阅读量', required=False)
    bcomment = serializers.IntegerField(label='评论量', required=False)
    image = serializers.ImageField(label='图片', required=False)

测试:

from booktest.serializers import BookInfoSerializer
data
= {'btitle': 'python'} serializer = BookInfoSerializer(data=data) serializer.is_valid() # False serializer.errors # {'btitle': [ErrorDetail(string='图书不是关于Django的', code='invalid')]}

 

=3  保存数据

1前面的验证数据成功后,我们可以使用序列化器来完成数据反序列化的过程.这个过程可以把数据转成模型类对象.

可以通过实现create()和update()两个方法来实现。

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    ...

    def create(self, validated_data):
        """新建"""
        return BookInfo(**validated_data)

    def update(self, instance, validated_data):
        """更新,instance为要更新的对象实例"""
        instance.btitle = validated_data.get('btitle', instance.btitle)
        instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
        instance.bread = validated_data.get('bread', instance.bread)
        instance.bcomment = validated_data.get('bcomment', instance.bcomment)
        return instance

如果需要在返回数据对象的时候,也将数据保存到数据库中,则可以进行如下修改

class BookInfoSerializer(serializers.Serializer):
    """图书数据序列化器"""
    ...

    def create(self, validated_data):
        """新建"""
        return BookInfo.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """更新,instance为要更新的对象实例"""
        instance.btitle = validated_data.get('btitle', instance.btitle)
        instance.bpub_date = validated_data.get('bpub_date', instance.bpub_date)
        instance.bread = validated_data.get('bread', instance.bread)
        instance.bcomment = validated_data.get('bcomment', instance.bcomment)
        instance.save()
        return instance

实现了上述两个方法后,在反序列化数据的时候,就可以通过save()方法返回一个数据对象实例了

book = serializer.save()

 

2如果创建序列化器对象的时候,没有传递instance实例,则调用save()方法的时候,create()被调用,相反,如果传递了instance实例,则调用save()方法的时候,update()被调用。

# views.py

from db.serializers import BookInfoSerializer
data = {'btitle': '封神演义'}
serializer = BookInfoSerializer(data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: 封神演义>


from db.models import BookInfo
book = BookInfo.objects.get(id=2)
data = {'btitle': '倚天剑'}
serializer = BookInfoSerializer(book, data=data)
serializer.is_valid()  # True
serializer.save()  # <BookInfo: 倚天剑>
book.btitle  # '倚天剑'

 

附加说明

1) 在对序列化器进行save()保存时,可以额外传递数据,这些数据可以在create()和update()中的validated_data参数获取到

# request.user 是django中记录当前登录用户的模型对象
serializer.save(owner=request.user)

 

2)默认序列化器必须传递所有required的字段,否则会抛出验证异常。但是我们可以使用 partial 参数来允许部分字段更新

# Update `comment` with partial data
serializer = CommentSerializer(comment, data={'content': u'foo bar'}, partial=True)

 

 

【总结-重难点】

1、定义序列化器——序列化/反序列化  read_only / write_only

2、创建Serializer对象——三大参数 instance / data / context

3、使用序列化器——

序列化— many=True /  safe=False

反序列化— validators=[ ] / partial 参数 / is_valid() / source / serializers.SerializerMethodField()

 

【练习代码笔记】

from django.db import models

# Create your models here.


class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32,null=True)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    # publish = models.CharField(max_length=32)
    publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)


    class Meta:
        db_table = "tb_book"
        verbose_name = "书籍"
        verbose_name_plural = verbose_name    # 复数

    # source的用法2
    def test(self):
        # python是强类型语言,不支持字符串和数字直接相加
        return self.title+str(self.price)

# source的用法3
class Publish(models.Model):
    name=models.CharField(max_length=32)
    addr=models.CharField(max_length=32)

    # 若想让前端显示为字段具体内容,而不是对象,则需要重写该字段的 __str__方法
    # 也可以在序列化器类的序列化字段中,在该字段的属性 使用 source='publish.name'

    def __str__(self):
        return self.name


# 1、db_column 指定了对应的字段名
#    db_table 指定了对应的表名
#
# 2、verbose_name / verbose_name_plural  指定在admin管理界面中显示中文;
# verbose_name表示单数形式的显示,
# verbose_name_plural表示复数形式的显示;
# 中文的单数和复数一般不作区别。

# 3、help_text="字段提示文本,如'请输入密码'等等"
models.py
"""drftest URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""

from django.contrib import admin
from django.urls import path,include,re_path
from test2_sers import views

urlpatterns = [
    path('admin/', admin.site.urls),
# 把子应用test1 中的路由文件加载到总路由文件中
    path('student/',include('test1.urls')),

# <1>【接口1:get-查询所有】 朝该地址发送 get请求,会拿到所有数据/
# <3>【接口3:新增一个   Book--------》post
    path('books/',views.Book.as_view()),

# <2>【接口2:get-查询单个】
    re_path('^books/(?P<id>d+)',views.BookDetail.as_view()),

]




# -----------------------------------------》
# 【-总结-】
# 【关于 模型表.as_view() 】
# 所有基于类的视图都必须继承View类,该类用来处理视图关联到URL的操作,具体分析如下:
#
# 由于django的URL解析器期望发送request以及相关参数给一个可调用的函数,而不是一个类,
# 所以基于类的视图有一个as_view()类方法(该方法继承自父类View),调用该方法会返回URL解析器所期望的函数,
# 该函数会在请求到达与关联模式匹配的URL时调用,就像调用视图函数一个样子。查看源码会发现调用该函数首先会创建一个MyView类的实例,然后
#
# 1、调用self.setup()方法,用于初始化一系列属性。
# 2、之后调用self.dispatch(),该方法查看request以确定本次请求是GET还是POST等,如果定义了匹配方法,则将请求转发给匹配方法;
# 如果没有,则引发HttpResponseNotAllowed。本例中将调用我们自定义的get与post方法来响应请求
#
# ps:基于面向对象的知识,上述方法,如果子类MyView未重写,则去父类View中寻找
urls.py
from django.shortcuts import render

# Create your views here.

from django.views import View

from rest_framework.views import APIView
from rest_framework.request import Request
from rest_framework.response import Response

from test2_sers import models
from test2_sers.serializers2 import BookSerializer

# from app应用名.序列化器类的py文件 import 自定义序列化器

'''
序列化器的使用--写五个接口
-<1>【接口1:查询所有   Book--------》get
-<2>【接口2:查询单个   BookDetail--》get  
-<3>【接口3:新增一个   Book--------》post
-<4>【接口4:删除一个   BookDetail--》delete
-<5>【接口5:修改一个   BookDetail--》put

'''


# CBV

# 1、获取多条数据
class Book(APIView):
    def get(self, request, *args, **kwargs):
        # \取出所有数据(queryset对象)
        res = models.Book.objects.all()

        # \创建序列化器对象—— BookSerializer对象
        # = 借助序列化器——实例化 自定义的序列化器类,得到序列化器对象
        ser = BookSerializer(instance=res, many=True)
        # print(type(ser))  # <class 'rest_framework.serializers.ListSerializer'>

        # \ 拿到了数据(queryset对象),通过序列化器对象处理后,得到的是字典
        # ser.data
        # 数据字典再经过 drf 的 Response,返回序列化好的json格式字符串,传给前端
        return Response(ser.data)

    # 3新增单个——使用最基础的序列化器,若要新增 必须重写create方法;若要修改,则必须重写 update方法
    def post(self,request):  # 关于post请求的数据格式-解说,见下方【总结】
        # post 提交的数据 都在request.data,且是个字典形式
        # <QueryDict: {'title': ['走去乡下过日子'], 'price': ['25'], 'publish': ['新世界出版社']}>
        print(request.data)
        # return Response('get it')

        # 使用序列化器进行反序列化时,需要对数据进行验证后,才能获取验证成功的数据或保存成模型类对象。
        ser=BookSerializer(data=request.data)  # 将需要反序列化的数据传给 data
        if ser.is_valid():  # 校验数据是否合法
            ser.save()      # 保存到数据库中----》运行,无法保存(因为未重写create/update方法)---》解决方法:在序列化器里 重写create方法
            return Response(ser.data)  # 保存成功 返回新增的当前对象
        else:
            # 没有校验通过的错误信息
            return Response(ser.errors)



class BookDetail(APIView):
    # 2、获取单个数据对象

    # def get(self, request, id, *args, **kwargs):  # 传参方式,两种都可以
    def get(self, request, id):
        res = models.Book.objects.all().filter(id=id).first()
        # 单个,去掉many=True
        # 加many=True和不加,ser不是同一个对象o~~
        ser = BookSerializer(instance=res)
        # print(type(ser))   # <class 'test2_sers.serializers2.BookSerializer'>
        return Response(ser.data)

    # 4修改单个对象
    def put(self,request,id):
        # 通过id取到对象
        res={'code':100,'msg':''}

        try:
            book=models.Book.objects.get(id=id)  # 不要犯错只写id
            ser=BookSerializer(instance=book,data=request.data)
            ser.is_valid(raise_exception=True)
            ser.save()
            res['msg']='修改成功'
            res['result']=ser.data

        except Exception as e:
            res['code']=101
            res['msg']=str(e)

        return Response(res)

    # 5删除单个
    def delete(self,request,id):
        response={'code':100,'msg':'删除成功'}  # 可根据需求 增加判断条件
        models.Book.objects.filter(id=id).delete()
        return Response(response)



# 【总结】
# -----------------------------------
# 1、序列化器的作用——drf的序列化/反序列化
# =1.
# 序列化, 序列化器会把模型对象转换成字典, 经过drf的Response以后变成json字符串
# -Book - -序列化器 - -->字典 - -通过drf: Response - -》json格式字符串 - -->传给前端

# =2.
# 反序列化, 把客户端发送过来的数据, 经过request以后变成字典, 序列化器可以把字典转成模型
# json格式数据 - --drf:Request -->字典 - --序列化器 - --》Book

# =3.
# 反序列化, 完成数据校验功能


# -----------------------------------
# 2、序列化器对象的参数说明
# ser=BookSerializer(instance=res,many=True)

# =1、instance=数据(queryset对象)
# 序列化时,将模型类对象传入instance参数

# =2、many=True
# = 如果结果是多条数据(多个对象),则要加上 many=True
# 如果是单个对象,则不写

# =3、其他参数
# data
# 反序列化时,将要被反序列化的数据传入data参数
# eg: serializer = Serializer(instance=None, data=empty, **kwarg)

# context
# 在构造Serializer对象时,还可通过context参数额外添加数据
# eg: serializer = AccountSerializer(account, context={'request': request})

# -----------------------------------
# 拓展:
# 接口的幂等性
# post 请求不是接口幂等的,每次请求都是不一样的,就算是请求同一数据
# put get 请求是接口幂等的,即多次操作得到的结果和单次操作的结果 是一样的

# -----------------------------------
# 【post请求的数据格式】

# 一.HttpRequest.body
#   1.1 当浏览器基于http协议的GET方法提交数据时(没有请求体一说),数据会按照k1=v1&k2=v2&k3=v3的格式放到url中,然后发送给django,django会将这些数据封装到request.GET中,注意此时的请求体request.body为空、无用
#
#   1.2 当浏览器基于http协议的POST方法提交数据时,数据会被放到请求体中发送给django,django会将接收到的请求体数据存放于HttpRequest.body属性中.
#   但该属性的值为Bytes类型(套接字数据传输都是bytes类型),而通常情况下直接处理Bytes、并从中提取有用数据的操作是复杂而繁琐的,好在django会对它做进一步的处理与封装以便我们更为方便地提取数据,具体如何处理呢?
#
#   当前端采用POST提交数据时,数据有三种常用编码格式,编码格式不同Django会有不同的处理方式
#   # 编码格式1:application/x-www-form-urlencoded,是form表单默认编码格式
#   # 编码格式2:multipart/form-data,上传文件专用格式
#   # 编码格式2:application/json,提交jason格式字符串
#
#   #====》I: 当POST数据的编码格式为application/x-www-form-urlencoded时《====
#       HttpRequest.body中的数据格式为b'a=1&b=2&c=3'
#       django会将其提取出来封装到request.POST中
#       request.FILES此时为空
#
#       如:
#       print(request.body) # b'a=1&b=2&c=3'
#       print(request.POST) # <QueryDict: {'a': ['1'],'b': ['2'],'c': ['3']}
#       print(request.FILES) # <MultiValueDict: {}>
#
#
#   #====》II: 当POST数据的编码格式为multipart/form-data时《====
#       详见:https://my.oschina.net/cnlw/blog/168466?fromerr=aQL9sTI2
#
#       HttpRequest.body中的数据格式为b'------WebKitFormBoundaryKtcwuksQltpNprep
Content-Disposition: form-data;......',注意,文件部分数据是流数据,所以不在浏览器中显示是正常的
#       django会将request.body中的非文件数据部分提取出来封装到request.POST中
#       将上传的文件部分数据专门提取出来封装到request.FILES属性中
#
#       如:
#       print(request.body) # 不要打印它,打印则报错,因为它是数据流
#       print(request.POST) # <QueryDict: {'a': ['1'],'b': ['2'],'c': ['3']}
#       print(request.FILES) # <MultiValueDict: {'head_img': [<InMemoryUploadedFile: 11111.jpg (image/jpeg)>]}>
#
#       强调:
#         1、毫无疑问,编码格式2的数据量要大于编码格式1,如果无需上传文件,还是推荐使用更为精简的编码格式1
#         2、FILES will only contain data if the request method was POST and the <form> that posted to the request had enctype="multipart/form-data". Otherwise, FILES will be a blank dictionary-like object.
#
#   #===》III: 当POST数据的编码格式为application/json时《====
#       此时在django后台,request.POST和request.FILES中是没有值的,都放到request.body中了,需要用json.loads对其进行反序列化
#
#       如:
#       print(request.body) # b'{"a":1,"b":2,"c":3}'
#       print(request.POST) # <QueryDict: {}>
#       print(request.FILES) # <MultiValueDict: {}>
#
#
#   1.3 如何设定POST提交数据的编码格式
#     前端往后台POST提交数据,常用技术有form表单和ajax两种
#     form表单可以设置的数据编码格式有:编码格式1、编码格式2
#     ajax可以设置的数据编码格式有:编码格式1、编码格式2、编码格式3
#
#     form表单可以通过属性enctype进行设置编码格,如下
#       编码格式1(默认的编码格式):enctype="application/x-www-form-urlencoded"
#       编码格式2(使用form表单上传文件时只能用该编码):enctype="multipart/form-data"
子应用的views.py
# 序列化器类---用于序列化Book表

# 导入序列化器
from rest_framework import serializers
# 导入异常-校验
from rest_framework.exceptions import ValidationError
from test2_sers import models

# 自定义校验规则,可以添加到序列化字段的validators属性
def check(data):
    if len(data)>10:
        raise ValidationError('最长不能超过10')
    else:
        return data

class BookSerializer(serializers.Serializer):
    # 1、列出要序列化的字段
    id = serializers.IntegerField(required=False)  # required=False 可以不传值
    # title=serializers.CharField(max_length=32,min_length=2,read_only=True)
    # source的用法1 ——修改字段前端显示的名字,真实的字段名是title,想让其在前端显示为name,则可以改写如下:
    name=serializers.CharField(max_length=32,min_length=2,read_only=True,source='title')

    price=serializers.DecimalField(max_digits=5, decimal_places=2)
    # 2、为字段增加校验规则 validators=[check,]
    # write_only=True 序列化时 不显示
    # publish=serializers.CharField(max_length=32,write_only=True,validators=[check,])
    # source的用法3 使用.的方法 获取字段数据
    publish_addr=serializers.CharField(max_length=32,source='publish.addr')  # objects.name

    # source的用法2 返回表模型中 方法的执行返回结果
    test_result=serializers.CharField(source='test')



    # 为什么要重写create?——新增数据
    # 因为继承了 serializers.Serializer,若想保存 新增的目标模型表的数据 就需要重写create
    def create(self, validated_data):
        res=models.Book.objects.create(**validated_data)
        print(res)
        return res

    # 重写 update方法 ——更新数据
    def update(self, instance, validated_data):   # instance 修改的对象,例如 book对象; validated_data 通过校验的反序列化的数据
        instance.title=validated_data.get('title')
        instance.price=validated_data.get('price')
        instance.publish=validated_data.get('publish')
        instance.save()   # 这个save() 不是drf的,是对象自己的
        return instance  # 一定要返回修改的数据对象



    # 局部钩子: validate_字段名
    # 需要带一个data,data就是该字段的数据
    # 例如 给 title字段增加一个局部钩子
    def validate_title(self,data):
        if data.startswith('sb'):
            raise ValidationError('不能以sb开头')
        else:
            return data

    # 全局钩子
    def validate(self, attrs):   # attrs 是全部数据
        # title=attrs['title']  # 字典取值 可以用key 也可以用 get
        title=attrs.get('title')
        publish=attrs.get('publish')
        if title==publish:
            raise ValidationError('书名不能跟出版社同名')
        else:
            return attrs







# 【总结】
#
# 1、字段属性
#     title=serializers.CharField(max_length=32,min_length=2,read_only=True)
#     price=serializers.DecimalField(max_digits=5, decimal_places=2)
#     publish=serializers.CharField(max_length=32,write_only=True)
#
# =1、CharField 必须要有的字段
# max_length
#
# =2、DecimalField 必须要有的字段
# max_digits
# decimal_places
#
# =3、关于 read_only/write_only
# ===序列化/反序列化
# 【序列化】 就是将对象转化方便传输和存储字节序列,例如json.dumps就是序列化(狭义的序列化,将字典转化为json字符串),
# 这样得到的json字符串不仅直接可以在其他语言使用(跨平台比较好),而且可以在前后端进行传输交互(drf序列化器)
#
# 【反序列化】 恰恰相反,而是将字节序列转化为对象,json.loads是将json字符串转化为字典,
# 是狭义的反序列化(因为在python, 一切皆对象,字典是dict( )),
# 而在项目中,前端传过来的序列化数据通过反序列化得到对象,进一步可以通过ORM操作,存入数据库。
#
# === read_only/write_only
# 【read_only】表明该字段仅用于序列化输出,默认 False
# read_only 是只用于读,不能写(可以理解为只能从后台到前台,后台操作后直接传前台),
# 不能对字段进行修改操作(也就是不和数据库进行交互),用在序列化字段里。
# 例如数据表中,gender只用于传到前端展示。而不用于存到数据库
#
# 【write_only】表明该字段仅用于反序列化输入,默认 False
# write_only是只写不能读,可以理解为只能前台到后台,
# 在后台做了逻辑操作后直接存数据库,在反序列化字段使用。
# 在数据表中,password就是反序列化,不需要传前台,
# 而re_password是自定义反序列化字段,仅用作后台和密码进行比较,
# 然后把结果返回前台,所以不存到数据库,在全局钩子使用时要pop掉
#
# 【解说版2】
# read_only
#  该字段仅用于 序列化输出(后端--》前端)
# (read_only=True)
# 即 后端往前端 传送数据时(序列化),该字段只用于显示在前端;
# 如果从前端往后端输入数据时(反序列化),该字段则可以不传值(这需要提前给该字段增加 null=True 属性,允许字段在反序列化输入时 可以为空,否则数据库会报错)
#
# write_only
# 该字段仅用于 反序列化输入 (前端--》后端)
# (write_only=True)
# 从前端往后端输入数据时(反序列化),该字段必须传值
# 后端往前端 传送序列化数据时(序列化),该字段不会显示在前端
#
# 用于 反序列化时必填,写入数据库的字段,但是不想后面在前端显示
# (一般若不想让字段显示在前端,是直接注释字段;但是这样可能会在反序列化时出问题,因此使用 write_only=True 来控制字段 )


# 2、source的用法
# source的用法1
# —— 修改字段前端显示的名字,真实的字段名是title,想让其在前端显示为name,则可以改写如下:
# 注意 设定的前端显示名 不能与真实的字段名相同,否则会报错
# name=serializers.CharField(max_length=32,min_length=2,read_only=True,source='title')
#
# source的用法2
# —— 若表模型中 定义有方法,则可以将方法执行的返回值 传给source
# 例如
# -1- models.py的book模型中,有个test方法
#     def test(self):
#         # python是强类型语言,不支持字符串和数字直接相加
#         return self.title+str(self.price)
#
# -2- 则在序列化器类中的 需要序列化字段中 加上一个字段用于接收方法执行的返回值
#     test_result=serializers.CharField(source='test')
#
# source的用法3 ——支持 xx.yy 的跨表操作
# 例如
# -1- models.py --两张一对多关系的publish表模型和 book表模型
# book表模型中,加上关联字段
#     publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)
#
# publish表模型
# class Publish(models.Model):
#     name=models.CharField(max_length=32)
#     addr=models.CharField(max_length=32)
#     def __str__(self):
#         return self.name
#
# book表模型的序列化器类-- source='publish.addr'
#     publish_addr=serializers.CharField(max_length=32,source='publish.addr')  # objects.name
#
子应用的serializers.py

 

 

三、模型序列化器 ModelSerializer 的使用 

1、模型序列化器 ModelSerializer 的使用

如果我们想要使用序列化器对应的是Django的模型类,DRF为我们提供了ModelSerializer模型类序列化器来帮助我们快速创建一个Serializer类。

ModelSerializer与常规的Serializer相同,但提供了:

  • 基于模型类自动生成一系列字段
  • 基于模型类自动为Serializer生成validators,比如unique_together
  • 包含默认的create()和update()的实现

1 原来用的Serilizer跟表模型没有直接联系, 模型类序列化器ModelSerilizer,跟表模型有对应关系

2 使用——模板 

class BookModelSerializer(serializers.ModelSerializer):
        class Meta:
            model=表模型    # 跟哪个表模型建立关系
            fields=[字段,字段] # 序列化的字段,反序列化的字段
            fields='__all__' # 所有字段都序列化,反序列化
            exclude=[字段,字段] # 排除哪些字段(不能跟fields同时使用)
            read_only_fields=['price','publish']  # 序列化显示的字段
            write_only_fields=['title']           # 反序列化需要传入的字段
            extra_kwargs ={'title':{'max_length':32,'write_only':True}}
            depth=1  # 了解,跨表1查询,最多建议写3
        # 重写某些字段
        publish = serializers.CharField(max_length=32,source='publish.name')
        # 局部钩子,全局钩子,跟原来完全一样

 

3 新增,修改
-统统不用重写create和update方法了,在ModelSerializer中重写了create和update

 

2、serializers.SerializerMethodField()的用法

 用法跟在 serializers.Serializer 里的一样

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'
  # 注意 不要写在了Meta类中!! publish = serializers.SerializerMethodField() def get_publish(self,obj): dic={'name':obj.publish.name,'addr':obj.publish.addr} return dic

 

3、序列化器类的嵌套——跨表获取数据

# 3 模型序列化器类的嵌套
class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model=models.Publish
        # fields='__all__'
        fields=['name','addr']

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'

    # publish = serializers.SerializerMethodField()
    # def get_publish(self,obj):
    #     dic={'name':obj.publish.name,'addr':obj.publish.addr}
    #     return dic
    # 序列化类的嵌套——会按照嵌套的序列化类的规则,序列化目标字段
    publish = PublishModelSerializer()

 4、Response的应用——自定义响应体类

class CommonResponse():
    def __init__(self):
        self.code=100   # 在调用该方法的序列化器类中,若code值变了,则修改;没有则新增/按默认值
        self.msg=''

    @property  # 将方法包装成数据属性,使用不需加括号,直接通过 对象.属性
    def get_dic(self):
        return self.__dict__
comResponse.py
# # 自定义Response类
# 数据操作的响应信息 写起来有些繁琐,可以自己定义响应体返回的数据的类,实例化得到对象 返回即可
# # 封装2---也可以自己封装一个response,继承drf 的 Response

# 封装1
# 新建python文件 comResponse.py,创建 CommonResponse类
# =1= 需要使用该类 先导入
from myModelSerializer.comResponse import CommonResponse
class BookDetail(APIView):
    def get(self, request, id):
        res = models.Book.objects.all().filter(id=id).first()
        ser = BookSerializer(instance=res)
        return Response(ser.data)

    def put(self, request, id):
        # =2= 实例化响应类 得到对象
        res=CommonResponse()
        try:
            book = models.Book.objects.get(id=id)
            ser = BookSerializer(instance=book, data=request.data)
            ser.is_valid(raise_exception=True)
            ser.save()
            # res['msg'] = '修改成功'
            # res['result'] = ser.data
            # =3= 对象.属性 获取响应信息
            res.code=100
            res.msg='修改成功'
            res.result=['hello','hi']  # 新增的数据/按照字典新增的方法

        except Exception as e:
            # res['code'] = 101
            # res['msg'] = str(e)
            res.code=101
            res.msg='未知错误'

        return Response(res.get_dic)

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


# ------------------------------------------------------
# Response 属性
# 响应状态码 status/content/等

from rest_framework.status import HTTP_201_CREATED
from rest_framework.settings import DEFAULTS  # 默认的drf控制界面的模板显示形式--JSON/浏览器

# 局部配置-drf控制界面的模板显示形式,粒度更小,仅用于局部;其余的未配置的则使用settings.py里的全局配置
from  rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer   # 局部配置-step1

class BookDetail(APIView):

    renderer_classes=[BrowsableAPIRenderer,]  # 局部配置-step2

    def get(self, request, id):
        res = models.Book.objects.all().filter(id=id).first()
        ser = BookSerializer(instance=res)
        return Response(ser.data)

    def put(self, request, id):
        res=CommonResponse()
        try:
            book = models.Book.objects.get(id=id)
            ser = BookModelSerializer(instance=book, data=request.data)
            ser.is_valid(raise_exception=True)
            ser.save()
            res.msg='成功'
            res.result=['sdsasdf','asdfa']

        except Exception as e:
            print(str(e))
            res.msg = '未知错误'
            res.code = 101

        return Response(data=res.get_dic,status=HTTP_201_CREATED)
    # Response(data=res.get_dic,status=HTTP_201_CREATED) 读源码/status.py

    def delete(self,request,id):
        response = {'code': 100, 'msg': '删除成功'}
        models.Book.objects.filter(id=id).delete()
        return Response(response)
子应用-views.py

【总结-练习代码】

from django.db import models

# Create your models here.

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32,null=True)
    price = models.DecimalField(max_digits=5, decimal_places=2,null=True)
    publish = models.ForeignKey(to='Publish',null=True,on_delete=models.CASCADE)

    def test(self):
        # python是强类型语言,不支持字符串和数字直接相加
        return self.title+str(self.price)


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

    def __str__(self):
        return self.name
models.py
from django.shortcuts import render

# Create your views here.
from rest_framework.views import APIView
from rest_framework.response import Response
from myModelSerializer import models
# from app01.serializer import BookModelSerializer as BookSerializer  # 一种替换序列化器的技巧
from myModelSerializer.serializer import BookModelSerializer
from myModelSerializer.serializer import BookSerializer

'''5个接口
1 查询所有   Book--》get
2 查询单个   BookDetali--》get
3 新增一个   Book--》post
4 删除一个   BookDetali--》delete
5 修改一个   BookDetali--》put   向地址books/1发送put请求
'''

class Book(APIView):
    def get(self, request, *args, **kwargs):
        res = models.Book.objects.all()
        # 借助序列化器
        # 如果是多条,就是many=True
        # 如果是单个对象,就不写
        # ser = BookSerializer(instance=res, many=True)
        ser = BookModelSerializer(instance=res, many=True)
        print(type(ser))  # rest_framework.serializers.ListSerializer
        # 通过序列化器得到的字典
        # ser.data
        print(ser.data)
        return Response(ser.data)

    def post(self, request):
        # post提交的数据都在request.data 是个字典
        print(request.data)
        # ser = BookSerializer(data=request.data)
        ser = BookModelSerializer(data=request.data)
        if ser.is_valid():  # 校验数据是否合法
            ser.save()  # 保存到数据库中
            return Response(ser.data)
        else:
            # 没有校验通过的错误信息
            return Response(ser.errors)





# ==============================================

class BookDetail(APIView):
    def get(self, request, id):
        res = models.Book.objects.all().filter(id=id).first()
        # 单个,去掉many=True
        # 加many=True和不加,ser不是同一个对象
        ser = BookSerializer(instance=res)
        print(type(ser))  # app01.serializer.BookSerializer
        return Response(ser.data)

    def put(self, request, id):
        # 通过id取到对象
        res = {'code': 100, 'msg': ''}
        try:
            book = models.Book.objects.get(id=id)
            ser = BookSerializer(instance=book, data=request.data)
            ser.is_valid(raise_exception=True)
            ser.save()
            res['msg'] = '修改成功'
            res['result'] = ser.data

        except Exception as e:
            res['code'] = 101
            res['msg'] = str(e)

        return Response(res)

    def delete(self,request,id):
        response = {'code': 100, 'msg': '删除成功'}
        models.Book.objects.filter(id=id).delete()
        return Response(response)
子应用-views.py
# 序列化器类(序列化Book表)
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from myModelSerializer import models

# 一、Serializer序列化器
# BookSerializer
class BookSerializer(serializers.Serializer):
    id=serializers.IntegerField(required=False)
    name=serializers.CharField(max_length=32,min_length=2,source='title')
    price=serializers.DecimalField(max_digits=5,decimal_places=2)
    publish=serializers.CharField(max_length=32,source='publish.name')
    publish_addr=serializers.CharField(source='publish.addr')
    # xxx=serializers.CharField(source='test')

    def create(self, validated_data):
        res=models.Book.objects.create(**validated_data)
        return res

    def update(self, instance, validated_data):
        instance.title=validated_data.get('title')
        instance.price=validated_data.get('price')
        instance.publish=validated_data.get('publish')
        instance.save()
        return instance

# 二、ModelSerializer 表模型序列化器
# ModelSerializer 表模型序列化器,与模型表一一对应的序列化器;即专用于指定的表模型;一个表,可以有多个序列化器
# 表模型序列化器,继承serializers.Serializer;新增/修改 不需要手动重写 反序列化的 create /update 方法,因为序列化器源码已经重写了 反序列化的 create /update 方法

# 1 BookModelSerializer
# version 1——序列化部分字段
class BookModelSerializer(serializers.ModelSerializer):
    # 3-1 重写'publish'
    publish=serializers.CharField(max_length=32,source='publish.name')
    # 3-2 新增'addr'字段
    addr=serializers.CharField(source='publish.addr')
    #1、内部类 Meta
    class Meta:
        # 2、与哪张表建立关系,注意1 :变量model不加s,否则就与内置的models冲突了
        model=models.Book
        # 3、需要序列化哪些字段
        # fields=['id','title','price']  # 模型表中已有的字段
        # 注意2:fields 为复数
        fields=['id','title','price','publish','addr']  # 重写 'publish',让其在前端显示具体出版社信息;新增 'addr' 字段,需要在模型序列化器里定义该字段

# version 2——序列化/反序列化 所有字段,支持改写已有字段,但是不支持增加新的序列化字段
class BookModelSerializer(serializers.ModelSerializer):
    publish = serializers.CharField(max_length=32, source='publish.name')
    class Meta:
        model=models.Book
        fields='__all__'

# version 3 —— 排除部分字段,不进行序列化
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model=models.Book
        # fields='__all__'
        exclude=['publish','price'] # 排除列表指定的字段,注意 不能和 fields 同时使用


# version 4 —— read_only_fields/write_only_fields/extra_kwargs
class BookModelSerializer(serializers.ModelSerializer):

    class Meta:
        model=models.Book
        fields='__all__'
        read_only_fields=['price','publish']  # 只用于序列化输出
        write_only_fields=['title',]  # 只用于反序列化输入 / 可能有的drf版本不能使用(实际使用再看啦)
        # extra_kwargs:给字段额外添加参数——在序列化器里重写字段属性比较麻烦,因此可以使用extra_kwargs
        # extra_kwargs={'title':{'max_length':64},'price':{'validators':[check,]}}
        extra_kwargs={'title':{'max_length':64,'write_only':True},}  # 也可以使用这种方式来指定write_only/write_only

    # 局部钩子/全局钩子 跟 Serializers序列化器的一样写法。注意:不要错写在内置类Meta类中!!


# 了解 # version 5 —— depth 连表查询返回结果
class BookModelSerializer(serializers.ModelSerializer):
    # publish = serializers.CharField(max_length=32, source='publish.name')
    class Meta:
        model = models.Book
        fields = '__all__'
        depth=1  # 连表查询,将关联的publish表的数据一次全部获取,而不会只返回出版社的id;如果book关联两张表,则depth可以设为2;
                # 个人建议depth最多不要超过3,尽量不要用


# 2 ModelSerializer中 serializers.SerializerMethodField 的用法
class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'

    publish = serializers.SerializerMethodField()
    def get_publish(self,obj):
        dic={'name':obj.publish.name,'addr':obj.publish.addr}
        return dic


# 3 模型序列化器类的嵌套
class PublishModelSerializer(serializers.ModelSerializer):
    class Meta:
        model=models.Publish
        # fields='__all__'
        fields=['name','addr']

class BookModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = '__all__'

    # publish = serializers.SerializerMethodField()
    # def get_publish(self,obj):
    #     dic={'name':obj.publish.name,'addr':obj.publish.addr}
    #     return dic
    # 序列化类的嵌套——会按照嵌套的序列化类的规则,序列化目标字段
    publish = PublishModelSerializer()
子应用-serializers.py

 

 

参考:python/Django-rest-framework框架/2-drf-序列化组件

关于定义序列化器时,read_only和write_only有什么作用

 

原文地址:https://www.cnblogs.com/bigorangecc/p/13924192.html