优秀的编程知识分享平台

网站首页 > 技术文章 正文

Django REST framework学习笔记(4)-序列化

nanyue 2025-02-13 13:30:36 技术文章 5 ℃

api接口开发中,最核心最常见的就是序列化,所谓的序列化就是把数据转换格式,常见的序列化方式,json,pickle,base64,struct。

REST framework中的序列化类与Django的FormModelForm类非常相似。

序列化可以分为两个阶段:

  • 序列化:把我们识别的数据转换成指定的格式提供给别人,例如:我们在django中获取到的数据默认是模型对象,但模型对象数据无法直接提供给前端,所有我们需要把数据进行序列化,变成json数据提供给别人。
  • 反序列化:把别人提供的数据,转换/还原成我们需要的格式。例如:前端js提供的json数据,对应python而言json就是字符串,我们需要反序列化成字典,然后接着字典再进行转换成模型对象,这样才能把数据保存到数据库中。

serializers是drf提供给开发者调用的序列化器模块,里面声明了所有的可用序列化器的基类

  • serializers.Serializer 序列化器基类,drf中所有的序列化器类都必须继承于Serializer
  • serializers.ModelSerializer 模型序列化器基类,是基于模型的序列化器,一般在操作表时用它

什么时候使用Serializer,什么时候使用ModelSerializer:主要看数据是否从数据库中获取,如果是则用ModelSerializer,否则使用Serializer

 #基本的API
  .data - 返回传出的原始表示。
  .is_valid() - 反序列化并验证传入的数据。
  .validated_data - 返回验证的传入数据。
  .errors - 在验证期间返回错误。
  .save() - 将验证的数据保存到对象实例中。
 #四种方法可以被覆盖
  .to_representation() - 重写此操作以支持序列化,用于读取操作。
  .to_internal_value() - 重写此操作以支持反序列化,以用于写入操作。
  .create() 和 .update() - 覆盖其中一个或两个以支持保存实例。

不使用序列化,需要自己写JsonResponse

 #1.model.py
 from django.db import models
 # 学生表
 class Student(models.Model):
     name = models.CharField(max_length=100, verbose_name='姓名')
     sex = models.BooleanField(default=1, verbose_name='性别')
     classmate = models.CharField(max_length=20, verbose_name='班级')
     desc = models.TextField(verbose_name='个性签名')
 
     class Meta:
         verbose_name_plural = '学生表'
 
 #2.views.py
 import json
 from django.views import View
 from django.http import JsonResponse, HttpResponse
 from . import models
 """
 编写2个视图类,一个带pk,一个不带pk
 """
 class StudentView(View):
     def post(self, request): #创建数据
         data = json.loads(request.body)
         ins = models.Student.objects.create(**data)
         return JsonResponse(data={
             "name": ins.name,
             "sex": ins.sex,
             "classmate": ins.classmate,
             "desc": ins.desc,
        }, status=200, safe=False)
 
     def get(self, request): #查询所有
         ins = list(models.Student.objects.values())
         return JsonResponse(data=ins, safe=False)
 
 class StudentInfoView(View):
     def get(self, request, pk): #查询一个
         ins = models.Student.objects.filter(pk=pk).first()
         print(ins.name)
         return JsonResponse(data={
             "name": ins.name,
             "sex": ins.sex,
             "classmate": ins.classmate,
             "desc": ins.desc,
        })
 
     def put(self, request, pk): #修改一个
         data = json.loads(request.body)
         try:
             ins = models.Student.objects.filter(pk=pk).update(**data)
             li = models.Student.objects.get(pk=ins)
             return JsonResponse(data={
                 "name": li.name,
                 "sex": li.sex,
                 "classmate": li.classmate,
                 "desc": li.desc, })
         except models.Student.DoesNotExist:
             return JsonResponse(data=None, status=404, safe=False)
 
     def delete(self, request, pk): #删除一个
         try:
             models.Student.objects.filter(pk=pk).delete()
             return JsonResponse(data={"msg": "删除成功"}, status=200, safe=False)
         except:
             return JsonResponse(data=None, status=404, safe=False)
 
 #3.urls.py
 from django.urls import path, re_path
 from . import views
 
 urlpatterns = [
     path('stu/', views.StudentView.as_view()),
     re_path(r'^stu/(?P\d+)/$', views.StudentInfoView.as_view()),
 ]

使用序列化器,model.py和上面的一样,

 #1.serializer.py #名字可以随意取,一般使用serializer
 from rest_framework import serializers
 from . import models
 
 class StudentModelSer(serializers.ModelSerializer):
     class Meta:
         model = models.Student
         fields = "__all__"
 
 #2.views.py
 from rest_framework.viewsets import ModelViewSet
 from . import models
 from . import serializer
 
 class StudentModelViewSet(ModelViewSet):
     queryset = models.Student.objects.all()
     serializer_class = serializer.StudentModelSer
 
 #3.urls.py
 from rest_framework.routers import SimpleRouter
 from . import views
 
 router = SimpleRouter()
 router.register('stu', views.StudentModelViewSet)
 
 urlpatterns = router.urls

Serializers

序列化器 — Serializer,drf框架最大的特点就是它序列化器,其作用如下:

  • 将模型对象序列化为字典,将来提供给视图经过response以后成为json字符串。
  • 将客户端发送过来的数据经过视图调用request成为python字典,序列化器可以把字典转换成模型。
  • 完成数据库校验和操作数据库功能

定义序列化器

 from rest_framework import serializers
 
 class CommentSerializer(serializers.Serializer):
     #1. 转换的字段声明,和Form非常像
     #字段 = serializers.字段类型(选项=选项值)
     email = serializers.EmailField()
     content = serializers.CharField(max_length=200)
     created = serializers.DateTimeField()
     
     #2.如果继承的是ModelSerializer,则需要声明调用的模型信息
     class Meta:
         model = 模型
         fields = ('字段1','字段2','字段3')
     
     #3.模型操作的方法
     def create(self, validated_data):
         #添加数据操作,自动实现从字典变成模型对象的过程
         pass
     def update(self, instance, validated_data):
         #更新数据操作
         pass
     
     #4.验证代码的对象方法
     def validate_字段名(self, data): #局部钩子,验证某个字段
         pass
     def validate(self, data): #全局钩子,验证对象
         pass
 
 #2.有时还需要能够表示更复杂的对象,其中对象的某些属性可能不是简单的数据类型,如字符串,日期或整数。
 class UserSerializer(serializers.Serializer):
     email = serializers.EmailField()
     username = serializers.CharField(max_length=100)
 
 class CommentSerializer(serializers.Serializer):
     user = UserSerializer()
     #上面的序列化,如果可以为空加上required=False如果是列表加上many=True
     content = serializers.CharField(max_length=200)
     created = serializers.DateTimeField()
 
 #传进入的json数据格式
 CommentSerializer(data={'user': {'email': 'foobar', 'username': 'doe'}, 'content': 'baz'})
 serializer.is_valid()

序列化

在服务器响应时,使用序列号器可以完成对数据的序列化,

 #1.models.py
 from django.db import models
 # 学生表
 class Student(models.Model):
     name = models.CharField(max_length=100, verbose_name='姓名')
     sex = models.BooleanField(default=1, verbose_name='性别')
     classmate = models.CharField(max_length=20, verbose_name='班级')
     desc = models.TextField(verbose_name='个性签名')
 
     class Meta:
         verbose_name_plural = '学生表'
 
 #2.serializer.py,序列化器
 from rest_framework import serializers
 class StuSer(serializers.Serializer):
     id = serializers.IntegerField()
     name = serializers.CharField()
     sex = serializers.BooleanField()
     classmate = serializers.CharField()
     desc = serializers.CharField()
 
 #3.views.py
 class StuView(View):
     def get(self, request):
         #1.获取表数据
         stu_list = models.Student.objects.all()
         #2.实例化序列化器,和form表单类似,序列化多个对象,应该传递many=True标志,反序列化默认支持多个对象
         ser = serializer.StuSer(stu_list, many=True)
         #3.获取转换后的数据
         data = ser.data
         #4.返回json数据
         return JsonResponse(data=data, status=200, safe=False)
 
 #4.urls.py
 urlpatterns = [
     path('stu/', views.StuView.as_view())
 ]
 
 #5.get请求访问url就得到json数据
 
 
 ser = serializer.StuSer(stu_list, many=True)
 data = ser.data
 #此时已经将模型转换为Python原生数据类型

反序列化

在客户端请求时,使用序列化器可以完成对数据的反序列化

 #1.serializer.py,补全字段参数
 class StuSer(serializers.Serializer):
     id = serializers.IntegerField(read_only=True)  # 反序列化不会要求ID字段
     name = serializers.CharField(required=True)  # 反序列化必填字段
     sex = serializers.BooleanField(default=True)  # 默认为True
     classmate = serializers.CharField(max_length=10, min_length=0)  # 最小0 最大10
     desc = serializers.CharField(allow_null=True, allow_blank=True)  # 允许不填
 
 #2.views.py,添加post函数
 class StuView(View):
     def post(self, request):
         # 1.查看请求进来的数据
         data = json.loads(request.body)
         # 2.序列化
         ser = serializer.StuSer(data=data)
         # 3.验证数据
         if ser.is_valid():
             # 4.数据验证通过,操作数据库
             return JsonResponse(dict(ser.data), status=200)
         # 5.序列化失败 返回错误
         return JsonResponse(dict(ser.errors), status=400)
     
 #在反序列化数据时,你总是需要在尝试访问验证数据之前调用is_valid(),或者保存对象实例。如果发生任何验证错误,那么.errors属性将包含一个代表错误消息的字典

数据验证

 #1.serializer.py,在原来的序列化文件里加上验证方法validate
 class StuSer(serializers.Serializer):
     id = serializers.IntegerField(read_only=True)  # 反序列化不会要求ID字段
     name = serializers.CharField(required=True)  # 反序列化必填字段
     sex = serializers.BooleanField(default=True)  # 默认为True
     classmate = serializers.CharField(max_length=10, min_length=0)  # 最小0 最大10
     desc = serializers.CharField(allow_null=True, allow_blank=True)  # 允许不填
 
     def validate_name(self, data):  # 校验单个字段
         # 验证单个字段,比如name不能是什么字符的,或者不能重复
         if data in ["python", 'django']:
             raise serializers.ValidationError(detail="名字不合法", code="validate_name")
         elif models.Student.objects.filter(name=data):
             raise serializers.ValidationError(detail="不能重复", code="validate_name")
         else:
             return data
 
     def validate(self, attrs):  # 校验所有字段
         # 验证204班只能是女生
         if attrs['classmate'] == '204' and attrs['sex']:
             raise serializers.ValidationError(detail='性别不符合', code='validate')
         return attrs

操作方法

.create().update() 方法都是可选的。您可以都不实现,或者实现其中的一个或两个,具体取决于你的序列化类的用例。

当反序列化数据时,我们可以调用 .save() 根据验证的数据返回一个对象实例,用来操作上面的方法。

 #1.serializer.py 添加create和update函数
 from rest_framework import serializers
 from stuapi import models
 
 class StuSer(serializers.Serializer):
     id = serializers.IntegerField(read_only=True)  # 反序列化不会要求ID字段
     name = serializers.CharField(required=True)  # 反序列化必填字段
     sex = serializers.BooleanField(default=True)  # 默认为True
     classmate = serializers.CharField(max_length=10, min_length=0)  # 最小0 最大10
     desc = serializers.CharField(allow_null=True, allow_blank=True)  # 允许不填
 
     def create(self, validated_data):
         """
        添加数据:方法名固定为create,
        validated_data: 验证成功的数据
        """
         stu = models.Student.objects.create(**validated_data)
         return stu
 
     def update(self, instance, validated_data):
         """
        更新数据:方法名固定为update
        instance: 实例化器的对象,必须传入的模型对象
        validated_data: 验证成功的数据
        """
         #instance.name = validated_data["name"]
         #instance.sex = validated_data["sex"]
         #instance.classmate = validated_data["classmate"]
         #instance.desc = validated_data["desc"]
         # 字段太多,可以这样写
         for key, value in validated_data.items():
             setattr(instance, key, value)
         instance.save()
         return instance
 
 #2.views.py
 class StuView(View):
     def post(self, request):
         # 1.查看请求进来的数据
         data = json.loads(request.body)
         # 2.序列化
         ser = serializer.StuSer(data=data)
         # 3.验证数据
         if ser.is_valid():
             # 4.数据验证通过,操作数据库
             ser.save()
             return JsonResponse(ser.data, status=200)
         # 5.序列化失败 返回错误
         return JsonResponse(dict(ser.errors), status=400)
 
     def put(self, request, pk):
         # 1.接受客户端请求进来的数据
         data = json.loads(request.body)
         try:
             stu = models.Student.objects.get(pk=pk)  # 2.查看数据库的数据
             ser = serializer.StuSer(instance=stu, data=data)  # 3.序列化
             #加上partial=True参数,部分更新
             #ser=serializer.StuSer(instance=stu,data=data,partial=True)
             if ser.is_valid():  # 4.验证数据
                 ser.save()  # 5.验证成功修改数据库,反序列化的时候,会返回一个数据对象实例
                 return JsonResponse(ser.data, status=200)
         except models.Student.DoesNotExist:
             return JsonResponse(data={"error": "没找到"}, status=400)
 
 #1.为嵌套表示书写.create()方法
 class UserSerializer(serializers.ModelSerializer):
     profile = ProfileSerializer()
 
     class Meta:
         model = User
         fields = ('username', 'email', 'profile')
 
     def create(self, validated_data):
         profile_data = validated_data.pop('profile')
         user = User.objects.create(**validated_data)
         Profile.objects.create(user=user, **profile_data)
         return user
 
 #2.为嵌套表示书写update()方法
 def update(self, instance, validated_data):
         profile_data = validated_data.pop('profile')
         profile = instance.profile
 
         instance.username = validated_data.get('username', instance.username)
         instance.email = validated_data.get('email', instance.email)
         instance.save()
 
         profile.is_premium_member = profile_data.get(
             'is_premium_member',
             profile.is_premium_member
        )
         profile.has_support_contract = profile_data.get(
             'has_support_contract',
             profile.has_support_contract
          )
         profile.save()
 
         return instance

字段类型

Boolean字段

BooleanField:表示一个 boolean 值。使用 HTML 编码表单时需要注意,省略一个 boolean 值被视为将字段设置为 False,即使它指定了 default=True 选项。这是因为 HTML 复选框通过省略该值来表示未选中的状态,所以 REST framework 将省略看作是空的复选框。请注意,将使用 required=False 选项生成默认的 BooleanField 实例(因为 Django models.BooleanField 始终为 blank=True)。如果想要更改此行为,请在序列化类上显式声明 BooleanField。对应与
django.db.models.fields.BooleanField.签名: BooleanField()

NullBooleanField:表示一个布尔值,它也接受 None 作为有效值。对应与
django.db.models.fields.NullBooleanField.签名: NullBooleanField()

字符串字段

CharField:表示文本。可以使用 max_length , min_length 验证(或限定)文本的长短。对应与
django.db.models.fields.CharField 或
django.db.models.fields.TextField.签名: CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True)

  • max_length - 验证输入所包含的字符数不超过这个数目。
  • min_length - 验证输入所包含的字符数不少于这个数目。
  • allow_blank - 如果设置为 True,则空字符串应被视为有效值。如果设置为 False,那么空字符串被认为是无效的并会引发验证错误。默认为 False。
  • trim_whitespace - 如果设置为 True,则前后空白将被删除。默认为 True。
  • allow_null 选项也可用于字符串字段,尽管它相对于 allow_blank 来说不被推荐。同时设置 allow_blank=True 和 allow_null=True 是有效的,但这样做意味着字符串表示允许有两种不同类型的空值,这可能导致数据不一致和微妙的应用程序错误。

EmailField:表示文本,将文本验证为有效的电子邮件地址。对应与
django.db.models.fields.EmailField签名: EmailField(max_length=None, min_length=None, allow_blank=False)

RegexField:表示文本,用于验证给定的值是否与某个正则表达式匹配。对应与
django.forms.fields.RegexField.签名: RegexField(regex, max_length=None, min_length=None, allow_blank=False)强制的 regex 参数可以是一个字符串,也可以是一个编译好的 Python 正则表达式对象。使用 Django 的
django.core.validators.RegexValidator 进行验证。

SlugField:一个根据模式 [a-zA-Z0-9_-]+ 验证输入的 RegexField 。对应与
django.db.models.fields.SlugField.签名: SlugField(max_length=50, min_length=None, allow_blank=False)

URLField:一个根据 URL 匹配模式验证输入的 RegexField。完全合格的 URL 格式为http:///对应与
django.db.models.fields.URLField. 使用 Django 的
django.core.validators.URLValidator 进行验证。签名: URLField(max_length=200, min_length=None, allow_blank=False)

UUIDField:确保输入的字段是有效的 UUID 字符串。to_internal_value 方法将返回一个 uuid.UUID 实例。在输出时,字段将以规范的连字符格式返回一个字符串,

 例如:"de305d54-75b4-431b-adb2-eb6b9e546013"

签名: UUIDField(format='hex_verbose')format: 确定 uuid 值的表示形式

  • 'hex_verbose' - 权威的十六进制表示形式,包含连字符: "5ce0e9a5-5ffa-654b-cee0-1238041fb31a"
  • 'hex' - 紧凑的十六进制表示形式, 不包含连字符:"5ce0e9a55ffa654bcee01238041fb31a"
  • 'int' - 128 位整数表示形式:"123456789012312313134124512351145145114"
  • 'urn' - RFC 4122 URN 表示形式: "urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a" 修改 format 仅影响表示值。所有格式都被 to_internal_value 接受。

FilePathField:一个其选项仅限于文件系统上某个目录中的文件名的字段。对应于
django.forms.fields.FilePathField.签名: FilePathField(path, match=None, recursive=False, allow_files=True, allow_folders=False, required=None, **kwargs)

  • path - FilePathField 应该从中选择的目录的绝对文件系统路径。
  • match - 用来过滤文件名的正则表达式,string 类型。
  • recursive - 指定是否应该包含路径的所有子目录。默认值是 False。
  • allow_files - 是否应该包含指定位置的文件。默认值为 True。这个参数或 allow_folders 必须是 True。(两个属性必须有一个为 true)
  • allow_folders - 是否应该包含指定位置的文件夹。默认值是 False。这个参数或 allow_files 必须是 True。(两个属性必须有一个为 true)

IPAddressField:确保输入是有效的 IPv4 或 IPv6 字符串。对应于
django.forms.fields.IPAddressField 和
django.forms.fields.GenericIPAddressField.签名: IPAddressField(protocol='both', unpack_ipv4=False, **options)

  • protocol 将有效输入限制为指定的协议。接受的值是 'both' (默认),'IPv4' 或 'IPv6' 。匹配不区分大小写。
  • unpack_ipv4 解压 IPv4 映射的地址,如 ::ffff:192.0.2.1。如果启用此选项,则该地址将解压到 192.0.2.1。 默认是禁用的。只能在 protocol 设置为 'both' 时使用。

数字字段

IntegerField:表示整数。对应于
django.db.models.fields.IntegerField,
django.db.models.fields.SmallIntegerField,
django.db.models.fields.PositiveIntegerField 和
django.db.models.fields.PositiveSmallIntegerField。签名: IntegerField(max_value=None, min_value=None)

  • max_value 验证所提供的数字不大于这个值。
  • min_value 验证所提供的数字不小于这个值。

FloatField:表示浮点。对应于
django.db.models.fields.FloatField.签名: FloatField(max_value=None, min_value=None)

  • max_value 验证所提供的数字不大于这个值。
  • min_value 验证所提供的数字不小于这个值。

DecimalField:表示十进制,由 Python 用 Decimal 实例表示。对应于
django.db.models.fields.DecimalField.签名: DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None)

  • max_digits 允许的最大位数。它必须是 None 或大于等于 decimal_places 的整数。
  • decimal_places 小数位数。
  • coerce_to_string 如果应返回字符串值,则设置为 True ;如果应返回 Decimal 对象,则设置为 False 。默认值与 COERCE_DECIMAL_TO_STRING settings key 的值相同,除非被覆盖,否则该值将为 True。如果序列化对象返回 Decimal 对象,则最终的输出格式将由渲染器决定。请注意,设置 localize 将强制该值为 True。
  • max_value 验证所提供的数字不大于这个值。
  • min_value 验证所提供的数字不小于这个值。
  • localize 设置为 True 以启用基于当前语言环境的输入和输出本地化。这也会迫使 coerce_to_string 为 True 。默认为 False 。请注意,如果你在 settings 文件中设置了 USE_L10N=True,则会启用数据格式化。
  • rounding 设置量化到配置精度时使用的舍入模式。 有效值是 decimal 模块舍入模式。默认为 None。
 #用法示例
 
 #1.若要验证数字到999,精确到 2 位小数,应该使用:
 serializers.DecimalField(max_digits=5, decimal_places=2)
 
 #2.用10位小数来验证数字不超过10亿:
 serializers.DecimalField(max_digits=19, decimal_places=10)
 
 #这个字段还接受一个可选参数,
 #coerce_to_string。如果设置为 True,则表示将以字符串形式输出。如果设置为 False,则表示将保留为 Decimal 实例,最终表示形式将由渲染器确定。
 #如果未设置,则默认设置为与 COERCE_DECIMAL_TO_STRING setting 相同的值,除非另行设置,否则该值为 True。

日期和时间字段

DateTimeField:表示日期和时间。对应于
django.db.models.fields.DateTimeField.签名: DateTimeField(format=
api_settings.DATETIME_FORMAT, input_formats=None)

  • format - 表示输出格式的字符串。如果未指定,则默认为与 DATETIME_FORMAT settings key 相同的值,除非设置,否则将为 'iso-8601'。设置为格式化字符串则表明 to_representation 返回值应该被强制为字符串输出。格式化字符串如下所述。将此值设置为 None 表示 Python datetime 对象应由 to_representation 返回。在这种情况下,日期时间编码将由渲染器确定。
  • input_formats - 表示可用于解析日期的输入格式的字符串列表。 如果未指定,则将使用 DATETIME_INPUT_FORMATS 设置,该设置默认为 ['iso-8601']。
 #使用 ModelSerializer 或 HyperlinkedModelSerializer 时,请注意,auto_now=True或 auto_now_add=True 的模型字段默认情况下将使用 read_only=True 。
 
 #如果想覆盖此行为,则需要在序列化类中明确声明 DateTimeField。例如:
 class CommentSerializer(serializers.ModelSerializer):
     created = serializers.DateTimeField()
 
     class Meta:
         model = Comment

DateField:表示日期。对应于
django.db.models.fields.DateField签名: DateField(format=api_settings.DATE_FORMAT, input_formats=None)

  • format - 表示输出格式的字符串。如果未指定,则默认为与 DATE_FORMAT settings key 相同的值,除非设置,否则将为 'iso-8601'。设置为格式化字符串则表明 to_representation 返回值应该被强制为字符串输出。格式化字符串如下所述。将此值设置为 None 表示 Python date 对象应由 to_representation 返回。在这种情况下,日期时间编码将由渲染器确定。
  • input_formats - 表示可用于解析日期的输入格式的字符串列表。 如果未指定,则将使用 DATE_INPUT_FORMATS 设置,该设置默认为 ['iso-8601']。DateField 格式化字符串格式化字符串可以是明确指定的 Python strftime 格式,也可以是使用 ISO 8601 风格 date 的特殊字符串 iso-8601 。(例如 '2013-01-29')

TimeField:表示时间。对应于
django.db.models.fields.TimeField签名: TimeField(format=api_settings.TIME_FORMAT, input_formats=None)

  • format - 表示输出格式的字符串。如果未指定,则默认为与 TIME_FORMAT settings key 相同的值,除非设置,否则将为 'iso-8601'。设置为格式化字符串则表明 to_representation 返回值应该被强制为字符串输出。格式化字符串如下所述。将此值设置为 None 表示 Python time 对象应由 to_representation 返回。在这种情况下,日期时间编码将由渲染器确定。
  • input_formats - 表示可用于解析日期的输入格式的字符串列表。 如果未指定,则将使用 TIME_INPUT_FORMATS 设置,该设置默认为 ['iso-8601']。

DurationField:表示持续时间。

对应于
django.db.models.fields.DurationField这些字段的 validated_data 将包含一个 datetime.timedelta 实例。该表示形式是遵循格式 '[DD] [HH:[MM:]]ss[.uuuuuu]' 的字符串。签名: DurationField()

选择字段

ChoiceField:可以从一个有限的选择中接受值的字段。如果相应的模型字段包含 choices=… 参数,则由 ModelSerializer 自动生成字段。签名: ChoiceField(choices)

  • choices - 有效值列表,或 (key, display_name) 元组列表。
  • allow_blank - 如果设置为 True,则空字符串应被视为有效值。如果设置为 False,那么空字符串被认为是无效的并会引发验证错误。默认是 False。
  • html_cutoff - 如果设置,这将是 HTML 选择下拉菜单中显示的选项的最大数量。可用于确保自动生成具有非常大可以选择的 ChoiceField,而不会阻止模板的渲染。默认是 None.
  • html_cutoff_text - 指定一个文本指示器,在截断列表时显示,比如在 HTML 选择下拉菜单中已经截断了最大数量的项目。默认就会显示 "More than {count} items…"

Allow_blank 和 allow_null 都是 ChoiceField 上的有效选项,但强烈建议只使用一个而不是两个都用。对于文本选择,allow_blank 应该是首选,allow_null 应该是数字或其他非文本选项的首选。

MultipleChoiceField:可以接受一组零、一个或多个值的字段,从有限的一组选择中选择。采取一个必填的参数。 to_internal_value 返回一个包含选定值的 set。签名: MultipleChoiceField(choices)

  • choices - 有效值列表,或 (key, display_name) 元组列表。
  • allow_blank - 如果设置为 True,则空字符串应被视为有效值。如果设置为 False,那么空字符串被认为是无效的并会引发验证错误。默认是 False。
  • html_cutoff - 如果设置,这将是 HTML 选择下拉菜单中显示的选项的最大数量。可用于确保自动生成具有非常大可以选择的 ChoiceField,而不会阻止模板的渲染。默认是 None.
  • html_cutoff_text - 指定一个文本指示器,在截断列表时显示,比如在 HTML 选择下拉菜单中已经截断了最大数量的项目。默认就会显示 "More than {count} items…"

Allow_blank 和 allow_null 都是 ChoiceField 上的有效选项,但强烈建议只使用一个而不是两个都用。对于文本选择,allow_blank 应该是首选,allow_null 应该是数字或其他非文本选项的首选。

文件上传字段

FileField和 ImageField 类只适用于 MultiPartParser 或 FileUploadParser 。大多数解析器,例如, e.g. JSON 不支持文件上传。 Django 的常规 FILE_UPLOAD_HANDLERS 用于处理上传的文件。

FileField:表示文件。执行 Django 的标准 FileField 验证。对应于
django.forms.fields.FileField.签名: FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

  • max_length - 指定文件名的最大长度。
  • allow_empty_file - 指定是否允许空文件。
  • use_url - 如果设置为 True,则 URL 字符串值将用于输出表示。如果设置为 False,则文件名字符串值将用于输出表示。默认为 UPLOADED_FILES_USE_URL settings key 的值,除非另有设置,否则为 True。

ImageField:表示图片。验证上传的文件内容是否匹配已知的图片格式。对应于
django.forms.fields.ImageField.签名: ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL)

  • max_length - 指定文件名的最大长度。
  • allow_empty_file - 指定是否允许空文件。
  • use_url - 如果设置为 True,则 URL 字符串值将用于输出表示。如果设置为 False,则文件名字符串值将用于输出表示。默认为 UPLOADED_FILES_USE_URL settings key 的值,除非另有设置,否则为 True。

需要 Pillow 库或 PIL 库。 建议使用 Pillow 库。 因为 PIL 已经不再维护。

复合字段

ListField:验证对象列表的字段类。签名: ListField(child=, min_length=None, max_length=None)

  • child - 应该用于验证列表中的对象的字段实例。如果未提供此参数,则列表中的对象将不会被验证。
  • min_length - 验证列表中包含的元素数量不少于这个数。
  • max_length - 验证列表中包含的元素数量不超过这个数。
 #例如,要验证整数列表:
 scores = serializers.ListField(
    child=serializers.IntegerField(min_value=0, max_value=100)
 )
 
 #ListField 类还支持一种声明式风格,允许编写可重用的列表字段类。
 class StringListField(serializers.ListField):
     child = serializers.CharField()
 我们现在可以在我们的应用程序中重新使用我们自定义的 StringListField 类,而无需为其提供 child 参数。

DictField:验证对象字典的字段类。DictField 中的键总是被假定为字符串值。签名: DictField(child=)

  • child - 应该用于验证字典中的值的字段实例。如果未提供此参数,则映射中的值将不会被验证。
 #例如,要创建一个验证字符串到字符串映射的字段,可以这样写:
 document = DictField(child=CharField())
 
 #你也可以像使用 ListField 一样使用声明式风格。例如:
 class DocumentField(DictField):
     child = CharField()

JSONField:验证传入的数据结构由有效 JSON 基元组成的字段类。在其二进制模式下,它将表示并验证 JSON 编码的二进制字符串。签名: JSONField(binary)

  • binary - 如果设置为 True,那么该字段将输出并验证 JSON 编码的字符串,而不是原始数据结构。默认是 False.

其他类型的字段

ReadOnlyField:只是简单地返回字段的值而不进行修改的字段类。当包含与属性相关的字段名而不是模型字段时,此字段默认与 ModelSerializer 一起使用。签名: ReadOnlyField()

#例如,如果 has_expired 是 Account 模型中的一个属性,则以下序列化程序会自动将其生成为 ReadOnlyField :

class AccountSerializer(serializers.ModelSerializer):
    class Meta:
        model = Account
        fields = ('id', 'account_name', 'has_expired')

HiddenField:不根据用户输入获取值的字段类,而是从默认值或可调用值中获取值。签名: HiddenField()

#例如,要包含始终提供当前时间的字段作为序列化类验证数据的一部分,则可以使用以下内容:
modified = serializers.HiddenField(default=timezone.now)

#如果需要根据某些预先提供的字段值运行某些验证,则通常只需要 HiddenField 类,而不是将所有这些字段公开给最终用户。

ModelField:可以绑定到任意模型字段的通用字段。ModelField 类将序列化/反序列化的任务委托给其关联的模型字段。该字段可用于为自定义模型字段创建序列化字段,而无需创建新的自定义序列化字段。ModelSerializer 使用此字段来对应自定义模型字段类。签名: ModelField(model_field=)ModelField 类通常用于内部使用,但如果需要,可以由 API 使用。为了正确实例化 ModelField,必须传递一个附加到实例化模型的字段。例如:ModelField(model_field=MyModel()._meta.get_field('custom_field'))

SerializerMethodField:这是一个只读字段。它通过调用它所连接的序列化类的方法来获得它的值。它可用于将任何类型的数据添加到对象的序列化表示中。签名: SerializerMethodField(method_name=None)

  • method_name - 要调用序列化对象的方法的名称。如果不包含,则默认为 get_.
#由 method_name 参数引用的序列化方法应该接受一个参数(除了 self),这是要序列化的对象。它应该返回你想要包含在对象的序列化表示中的任何内容。例如:
from django.contrib.auth.models import User
from django.utils.timezone import now
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    days_since_joined = serializers.SerializerMethodField()

    class Meta:
        model = User

    def get_days_since_joined(self, obj):
        return (now() - obj.date_joined).days

序列化嵌套字段

#1.models.py模型表
class Album(models.Model):
    album_name = models.CharField(max_length=100)
    artist = models.CharField(max_length=100)

class Track(models.Model):
    album = models.ForeignKey(Album, related_name='tracks', on_delete=models.CASCADE)
    order = models.IntegerField()
    title = models.CharField(max_length=100)
    duration = models.IntegerField()

    class Meta:
        unique_together = ('album', 'order')
        ordering = ['order']

    def __unicode__(self):
        return '%d: %s' % (self.order, self.title)

StringRelatedField 用于使用 __unicode__ 方法表示关系。

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.StringRelatedField(many=True)

    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')

#参数:
#many - 如果是一对多的关系,就将此参数设置为True

PrimaryKeyRelatedField 用于使用其主键表示关系。

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')

#参数:
queryset - 验证字段输入时用于模型实例查询的查询集。必须显式地设置查询集,或设置read_only=True。
many - 如果应用于一对多关系,则应将此参数设置为True。
allow_null - 如果设置为True,那么该字段将接受None 值或可为空的关系的空字符串。默认为False。
pk_field - 设置为一个字段来控制主键值的序列化/反序列化。例如, pk_field=UUIDField(format='hex') 会将UUID主键序列化为其紧凑的十六进制表示形式。

HyperlinkedRelatedField 用于使用超链接来表示关系。

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.HyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name='track-detail'
    )

    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')

#参数:
view_name - 用作关系目标的视图名称。如果你使用的是标准路由器类,则这将是一个格式为 -detail 的字符串。必填.
queryset - 验证字段输入时用于模型实例查询的查询集。必须显式地设置查询集,或设置 read_only=True。
many - 如果应用于一对多关系,则应将此参数设置为 True。
allow_null - 如果设置为 True,那么该字段将接受 None 值或可为空的关系的空字符串。默认为 False。
lookup_field - 用于查找的目标字段。对应于引用视图上的 URL 关键字参数。默认是 'pk'.
lookup_url_kwarg - 与查找字段对应的 URL conf 中定义的关键字参数的名称。默认使用与 lookup_field 相同的值。
format - 如果使用格式后缀,则超链接字段将使用与目标相同的格式后缀,除非使用 format 参数进行覆盖。

SlugRelatedField 用于使用目标上的字段来表示关系。

class AlbumSerializer(serializers.ModelSerializer):
    tracks = serializers.SlugRelatedField(
        many=True,
        read_only=True,
        slug_field='title'
     )

    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'tracks')

#参数:
slug_field - 用来表示目标的字段。这应该是唯一标识给定实例的字段。例如, username。必填
queryset - 验证字段输入时用于模型实例查询的查询集。必须显式地设置查询集,或设置 read_only=True。
many - 如果应用于一对多关系,则应将此参数设置为 True。
allow_null - 如果设置为 True,那么该字段将接受 None 值或可为空的关系的空字符串。默认为 False。


HyperlinkedModelSerializer
作为身份关系应用,也可以用于对象的属性

class AlbumSerializer(serializers.HyperlinkedModelSerializer):
    track_listing = serializers.HyperlinkedIdentityField(view_name='track-list')

    class Meta:
        model = Album
        fields = ('album_name', 'artist', 'track_listing')

#参数:
view_name - 用作关系目标的视图名称。如果你使用的是标准路由器类,则这将是一个格式为 -detail 的字符串。必填。
lookup_field - 用于查找的目标字段。对应于引用视图上的 URL 关键字参数。默认是 'pk'。
lookup_url_kwarg - 与查找字段对应的 URL conf 中定义的关键字参数的名称。默认使用与 lookup_field 相同的值。
format - 如果使用格式后缀,则超链接字段将使用与目标相同的格式后缀,除非使用 format 参数进行覆盖。

自定义字段

 #代表 RGB 颜色值的类的例子
 
 class Color(object):
     """
    A color represented in the RGB colorspace.
    """
     def __init__(self, red, green, blue):
         assert(red >= 0 and green >= 0 and blue >= 0)
         assert(red < 256 and green < 256 and blue < 256)
         self.red, self.green, self.blue = red, green, blue
 
 class ColorField(serializers.Field):
     """
    Color objects are serialized into 'rgb(#, #, #)' notation.
    """
     def to_representation(self, obj):
         return "rgb(%d, %d, %d)" % (obj.red, obj.green, obj.blue)
 
     def to_internal_value(self, data):
         data = data.strip('rgb(').rstrip(')')
         red, green, blue = [int(col) for col in data.split(',')]
         return Color(red, green, blue)

字段参数

每个序列化字段类的构造函数都需要一些参数。某些字段类需要附加特定于该字段的参数,但应始终接受以下参数。

选项参数

  • max_length:最大长度
  • min_length:最小长度
  • max_value:最大值
  • min_value:最小值
  • allow_blank:是否允许为空
  • trim_whitespace:是否截断空白符

通用参数:

read_only:只读字段,默认为False,仅用于序列化输出序列化类输入中错误的包含'read_only'会被忽略。将其设置为True可确保在序列化表示时使用该字段,但在反序列化期间创建或更新实例时不使用该字段。

write_only:默认为False,仅用于反序列化输入将其设置为True以确保在更新或创建实例时可以使用该字段,但在序列化表示时不包括该字段。

required:默认为True,如果在反序列化过程中没有该提供字段,通常会出现错误。如果在反序列化过程中不需要此字段,则应该设置为false。将此设置为False还允许在序列化实例时从输出中省略对象属性或字典密钥。如果密钥不存在,它将不会包含在输出表示中。

allow_null:默认为False,如果把None传递给序列化字段,通常会引发错误。如果None应被视为有效值,则将此关键字参数设置为True。请注意,将此参数设置为True将意味着序列化输出的缺省值为null,但并不意味着输入反序列化的缺省值。

default:如果设置,则会给出默认值,在没有提供输入值时,将使用该默认值。如果未设置,则默认行为是不填充该属性。

 部分更新操作时不应该使用default。因为有些情况下,只有传入数据中提供的字段才会返回验证值。
 可以设置为函数或其他可调用的对象,在这种情况下,每次使用该值时都会对其进行调用。被调用时,它将不会收到任何参数。如果可调用对象具有set_context方法,那么在每次将字段实例作为参数获取值之前都会调用该方法。这与验证器的工作方式相同。
 在序列化实例时,如果对象属性或字典关键字不存在于实例中,将使用缺省值。
 
 请注意,设置默认值意味着该字段不是必需的。同时包括default和required的关键字参数都是无效的,会引发错误。

source:将用于填充字段的属性的名称。可以是一个只接受self参数的方法,如URLField(source='get_absolute_url'),或者使用点符号来遍历属性,如EmailField(source='user.email')。在使用点符号时,如果在属性遍历期间任何对象不存在或为空,则可能需要提供缺省值。source='*'具有特殊含义,用于表示整个对象应该传递到该字段。这对创建嵌套表示或对于需要访问完整对象以确定输出表示的字段非常有用。

validators:应该应用于传入字段输入的验证函数列表,该列表中的函数应该引发验证错误或仅返回。验证器函数通常应该引发
serializers.ValidationError,但Django的内置ValidationError也支持与Django代码库或第三方Django包中定义的验证器兼容。

 def check_classmate(data):
     if data == 202:
         raise serializers.ValidationError(detail="这个班级已招满", code='check_classmate')
     return data
 
 class StuSer(serializers.Serializer):
     classmate = serializers.CharField(max_length=10, min_length=0, validators=[check_classmate])  # 最小0 最大10

error_messages:一个字典,key是错误代码,value是对应的错误信息。

label:一个简短的文本字符串,可用作HTML表单字段或其他描述性元素中字段的名称。

help_text:一个文本字符串,可用作HTML表单字段或其他描述性元素中字段的描述。

initial:应该用于预填充HTML表单字段的值。你可能会传递一个可调用对象,就像你对任何常规DjangoField所做的一样

 importdatetime
 fromrest_frameworkimportserializers
 classExampleSerializer(serializers.Serializer):
 day=serializers.DateField(initial=datetime.date.today)

style:可用于控制渲染器渲染字段的键值对的字典。

 #这里有两个例子是'input_type'和'base_template':
 
 #Usefortheinput.
 
 password=serializers.CharField(style={'input_type':'password'})
 
 #Usearadioinputinsteadofaselectinput.
 
 color_channel=serializers.ChoiceField(
   choices=['red','green','blue'],
   style={'base_template':'radio.html'}
 )

ModelSerializer

如果要使用序列化器对应django的模型,一般使用ModelSerializer模型类序列化器。它自动创建一个 Serializer 类,其中的字段与模型类字段对应。

ModelSerializer类与常规Serializer类相同,不同之处在于:

  • 基于模型类自动生成一系列字段
  • 基于模型类自动为Serializer生成validators
  • 包含默认的create()和update()
 #1.定义方法和Serializer基本一样
 
 class StudentModelSer(serializers.ModelSerializer):
     # 1.转换字段的声明,重写模型的字段
 
     # 2.需要声明调用的模型信息
     class Meta:
         model = models.Student #对象模型,必填
         fields = "__all__"  #字段列表,必填
         #fields = ["name", "sex"] #也可以列出需要的字段
         exclude = ["id", "name"] #排查不需要的字段
         
         depth = 1 #设置为一个整数值,该值表示关联的深度
         
         read_only_fields = [] #只读字段,只有序列化的时候才用到
         
         extra_kwargs = {  #字段额外选项声明
             "age": {
                 "min_value": 10,
                 "max_value": 20,
                 "write_only": True,
                 "error_messages": {
                     "min_value": "不能小于10",
                     "max_value": "不能大于20",
                },
            }
        }
 
     # 3.验证对象的方法
     def validate_字段名(self, data): #局部钩子,验证某个字段
         pass
     def validate(self, data): #全局钩子,验证对象
         pass
 
     # 4.模型操作的方法
     def create(self, validated_data):
      pass #特别处理的情况需要重新,比如注册账号时的密码
 def update(self, validated_data):
      pass #特别处理的情况需要重新,比如注册账号时的密码
 
 
 #2.额外增加字段
 class AccountSerializer(serializers.ModelSerializer):
     url = serializers.CharField(source='get_absolute_url', read_only=True)
     groups = serializers.PrimaryKeyRelatedField(many=True)
 
     class Meta:
         model = Account
         fields = ["username", "url", "groups"]
 
 #总结:ModelSerializer,默认生成关联的模型表里的所有字段
 #配置
 class Meta:
 model=表名
 fields="__all__"/["字段名",]
 exclude=["字段名",]
 depth=1 外键关系找一层 会让外键关系字段变成read_only=True
 extra_kwargs={"字段名": {配置的属性}}
 
 SerializerMethodField()
 方法字段 会调用它自己的钩子方法,把方法的返回值给字段
 def get_字段名(self, obj):
 循环序列化的每个模型对象就是obj
 对obj进行ORM操作
 return 自定义的数据结构
 
 #3.查看自己创建的字段和验证器
 from .serializers import AccountSerializer
 serializer = AccountSerializer()
 print(repr(serializer))

序列化嵌套

在序列化器使用中,一般一个序列化器对应一个模型,往往模型之间存在外键关联,所以在输出数据时,不仅要获取当前模型的数据,还要把关联的模型数据同时返回。在这样的情况下,我们可以通过序列化器嵌套调用的方式,把我们当前模型的数据和外键对应的模型数据同时进行转换。

关系字段用于表示模型关系。 它们可以应用于ForeignKeyManyToManyFieldOneToOneField关系,反向关系以及GenericForeignKey等自定义关系。

在使用 ModelSerializer 类时,将自动为你生成序列化字段和关系。

 #1.models.py使用的表模型
 from django.db import models
 
 class BaseModel(models.Model):
     is_delete = models.BooleanField(default=False)  # 是否删除
     create_time = models.DateTimeField(auto_now_add=True)  # auto_now_add=True 只要创建,不需要手动插入时间,自动把当前时间插入
     last_update_time = models.DateTimeField(auto_now=True)  # auto_now=True,只要更新,就会把当前时间插入
 
     class Meta:
         abstract = True  # 抽象表,不再数据库建立出表
 
 # 书籍表
 class Book(BaseModel):
     title = models.CharField(verbose_name="图书", max_length=32)  # 书名
     price = models.DecimalField(verbose_name="价格", max_digits=8, decimal_places=2)  # 价格 小数保留后2位
     pub_date = models.DateField(verbose_name="出版日期")
     publish = models.ForeignKey(verbose_name="出版社", to="Publish", on_delete=models.CASCADE)  # 出版社和书是一对多 在多的表上关联,
     authors = models.ManyToManyField(verbose_name="作者", to="Author")  # 作者和书是多对多的关系,自动创建关联表
 
     def __str__(self):
         return self.title
 
 # 出版社
 class Publish(BaseModel):
     name = models.CharField(verbose_name="出版社", max_length=32)  # 出版社
     city = models.CharField(verbose_name="所在地", max_length=64)  # 地址
 
     def __str__(self):
         return self.name
 
 # 作者表
 class Author(BaseModel):
     name = models.CharField(verbose_name="姓名", max_length=32)  # 姓名
     author_detail = models.OneToOneField(verbose_name="详细信息", to='AuthorDetail', on_delete=models.CASCADE)
 
     def __str__(self):
         return self.name
 
 # 作者详细
 class AuthorDetail(BaseModel):
     sex_choices = ((0, "女"), (1, "男"),)  # 性别选择
     sex = models.SmallIntegerField(verbose_name="性别", choices=sex_choices)  # 性别
     mobile_tel = models.CharField(verbose_name="手机号码", max_length=20)  # 手机号码
 
     def __str__(self):
         return self.mobile_tel

方式一:序列化中写,外键关联的数据

 #2.serializer.py
 from rest_framework import serializers
 from . import models
 
 # 时间格式化,自定义成我们需要的格式format
 class BaseSer(serializers.Serializer):  
     create_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
     last_update_time = serializers.DateTimeField(format="%Y-%m-%d %H:%M:%S", required=False, read_only=True)
 
 
 class DetailModelSer(serializers.ModelSerializer):
     # Choise字段,显示的不是男/女,需要我们返回成汉字
     sex_display = serializers.SerializerMethodField(read_only=True)
 # 使用这个方法
     def get_sex_display(self, obj):
         return obj.get_sex_display()
 
     class Meta:
         model = models.AuthorDetail
         fields = ["sex_display", "mobile_tel"]
 
 
 class AuthorModelSer(serializers.ModelSerializer):
     # author_detail = DetailModelSer()
     # source取出关联的数据
     sex = serializers.CharField(source='author_detail.sex')
     mobile_tel = serializers.CharField(source='author_detail.mobile_tel')
     """
    #1.author_detail指定序列化器的返回格式
    "authors": [
        {
            "name": "灭绝师太",
            "author_detail": {
                "sex_display": "女",
                "mobile_tel": "17602199367"
            }
        }
    ],
    #2.使用source字段的返回格式如下
    "authors": [
        {
            "name": "灭绝师太",
            "sex": "0",
            "mobile_tel": "17602199367"
        }
    ]
    """
 
     class Meta:
         model = models.Author
         # fields = ["name", "author_detail"]
         fields = ["name", "sex", "mobile_tel"]
         # depth = 1 #指定深度,默认帮我们展开所有的字段
         # book > publish = 深度1层
         # book > author > author_detail = 深度2层
 
 
 class PublishModelSer(serializers.ModelSerializer):
     class Meta:
         model = models.Publish
         fields = ["name", "city"]
 
 
 class BookModelSer(serializers.ModelSerializer, BaseSer):
 _publish = serializers.SerializerMethodField(read_only=True)
     _authors = serializers.SerializerMethodField(read_only=True)
 
     def get__publish(self, obj):  # get_字段名
         return {'name': obj.publish.name, 'city': obj.publish.city}  # 返回出版社详情
 
     def get__authors(self, obj):  # get_字段名, 多对多的数据
         return [
            {
                 "name": i.name,
                 "sex": i.author_detail.get_sex_display(),
                 "age": i.author_detail.mobile_tel
            } for i in obj.authors.all()
        ]
 
     class Meta:
         model = models.Book
         fields = ("title", "price", "pub_date", "create_time", "last_update_time",
                   "publish", "_publish", "authors", "_authors")
         extra_kwargs = {
             'publish': {'write_only': True},
             '_publish': {'read_only': True},
             'authors': {'write_only': True},
             '_authors': {'read_only': True}
        }
         
 #3.views.py
 class BookModelViewSet(ModelViewSet):
     queryset = models.Book.objects.all()
     serializer_class = serializer.BookModelSer
 
 #4.验证
 ##1.get请求获取数据
 {
     "title": "降龙十八掌",
     "price": "33.50",
     "pub_date": "2022-02-28",
     "create_time": "2022-04-28 20:21:39",
     "last_update_time": "2022-04-28 20:21:39",
     "_publish": {
         "name": "丐帮",
         "city": "西京洛阳"
    },
     "_authors": [
        {
             "name": "乔峰",
             "sex": "男",
             "mobile_tel": 15678887772
        },
        {
             "name": "洪七公",
             "sex": "男",
             "mobile_tel": 13877666771
        }
    ]
 }
 
 ##2.post提交数据,提交的数据格式
 {
 "title": "金刚不坏",
 "price": "33.50",
 "pub_date": "2022-04-28",
 "publish": 4,
 "authors": [
 4
 ]
 }
 #提交成功,返回数据格式
 {
     "title": "金刚不坏",
     "price": "33.50",
     "pub_date": "2022-04-28",
     "create_time": "2022-04-28 20:58:36",
     "last_update_time": "2022-04-28 20:58:36",
     "_publish": {
         "name": "华山派",
         "addr": "华阴市"
    },
     "_authors": [
        {
             "name": "东方不败",
             "sex": "女",
             "age": 35
        }
    ]
 }

方式二:模型中写一个属性

 #1.models.py,在models中自定义模型属性方法,完成嵌套的操作
 # 书籍表
 class Book(BaseModel):
     title = models.CharField(verbose_name="图书", max_length=32)  # 书名
     price = models.DecimalField(verbose_name="价格", max_digits=8, decimal_places=2)  # 价格 小数保留后2位
     pub_date = models.DateField(verbose_name="出版日期")
     publish = models.ForeignKey(verbose_name="出版社", to="Publish", on_delete=models.CASCADE)  # 出版社和书是一对多 在多的表上关联,
     authors = models.ManyToManyField(verbose_name="作者", to="Author")  # 作者和书是多对多的关系,自动创建关联表
 
     def __str__(self):
         return self.title
 
     @property
     def get_publish(self): #返回出版社的字段
         return {
             "name": self.publish.name,
             "city": self.publish.city,
        }
 
     @property
     def get_author(self): #多对多的表
         print(self.authors.values())
         print(self.authors.all())
         return [
            {
                 "name": i.name,
                 "sex": i.author_detail.get_sex_display(),
                 "mobile_tel": i.author_detail.mobile_tel,
              } for i in self.authors.all()
        ]
 
 #2.Serializer.py
 class BookModelSer(serializers.ModelSerializer, BaseSer):
     #publish = PublishModelSer()
     #authors = AuthorModelSer(many=True)
 
     class Meta:
         model = models.Book
         fields = ['id', 'create_time', 'last_update_time',
                   'title', 'price', 'publish', 'authors',
                   'get_publish', 'get_author'] #2个模型里写的属性
 extra_kwargs = {
             'publish': {'write_only': True},
             'get_publish': {'read_only': True},
             'authors': {'write_only': True},
             'get_author': {'read_only': True},
        }
 
 #3.views.py
 class BookModelViewSet(ModelViewSet):
     queryset = models.Book.objects.all()
     serializer_class = serializer.BookModelSer
最近发表
标签列表