03-序列化详细
序列化常见字段
和models里面的字段是一一对应的,知识多出了两个字段
多出了俩 ListField DictField
字段 | 字段构造方式 |
---|---|
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=) |
序列化常见字段参数
作用: 用来做反序列化校验的。
通用字段
required 表明该字段在反序列化时必须输入,默认为True
defatult 反序列化时使用的默认值
allow_null 表明该字段是否允许传入None,默认False
validators 验证字段的合法性
erorr_message 包含错误变化于错误信息的字典
label 用于HTML展示API页面时,显示的字段名称
help_text 用于HTML展示API页面时,显示的字段帮助提示信息
--------- 非常重要 ----------
read_only 表明该字段仅用于序列化输出,默认False,一般是给前端数据的时候使用
write_one 表明该字段仅用于反序列化输入,默认False 一般用于往数据库里面存放数据
# CharField
max_length 最大长度
min_length 最小长度
allow_length 是否允许为空
trim_withspace 是否截断空白字符
创建表
# models.py
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name="书籍名称")
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="书籍价格")
publish_date = models.DateField(verbose_name="出版日期")
publish = models.ForeignKey(to='publish', on_delete=models.CASCADE, verbose_name="外键,关联出版社,设置级联更新和级联删除")
# 数据库中不会有这个字段。
# 会生成第三张表 authors
authors = models.ManyToManyField(to="Authors")
class Meta:
db_table = "book"
class Publish(models.Model):
name = models.CharField(max_length=32, verbose_name='出版社名称')
addr = models.CharField(max_length=32, verbose_name="出版社地址")
class Meta:
db_table = "publish"
class Authors(models.Model):
name = models.CharField(max_length=32, verbose_name='作者姓名')
age = models.IntegerField(verbose_name="作者的年龄")
class Meta:
db_table = "authors"
验证合法性 validators 字段自己的校验 (很少用到)
会把字段的值,传到指定的函数里面去做校验,如果验证通过正常返回,如果验证失败触发异常。 定义的函数形参名称随意。
# 如何使用
# 在类外面定义一个函数,函数放一个形参,名称可以随意
# 在对应的字段中,validators=[ 创建的函数 ], 比如 name = serializers.CharField(validators=[check_field])
from rest_framework import serializers
from datetime import datetime
from rest_framework.exceptions import ValidationError
# 自己定义的函数,用于给validators校验
def check_name(text):
if "sb" in text:
raise ValidationError("包含不合法的输入 sb ")
else:
return text
class BookSerializer(serializers.Serializer):
name = serializers.CharField(max_length=8, validators=[check_name]) # 支持放入多个validators=[check_name, check_length...]
price = serializers.DecimalField(max_digits=5, decimal_places=2)
# 这里的allow_null如果设置了,一般需要结合默认值一起去设置才会生效
publish_date = serializers.DateField(allow_null=True, default=datetime.now)
自定义错误信息返回字典 error_message
# 自定义序列化类.py
name = serializers.CharField(max_length=8, min_length=2, error_messages={"max_length": "书名最多8位", "min_length": "书名最少2位"})
# 前端报错信息
{
"code": 100,
"msg": {
"name": [
"书名最少2位"
]
}
}
返回定制字段
自定义返回的名称 source
# book_name 表示代表显示在前端的字段, source=name 这个name就表示在模型层中建立的字段
book_name = serializers.CharField(source="name")
# 可以跨表查询
publish_name = serializers.CharField(source="publish.name")
publish_addr = serializers.CharField(source='publish.addr')
# 所有的字段,都可以设置转成CharField
# 可以 但是非特殊情况不建议 建议按照规范去操作
# publish_id = serializers.CharField(source="publish.id")
publish_id = serializers.IntegerField(source="publish.id")
ListField 和 DictField
- 单个结果用字典,即DictField
- 多个结果用列表,即ListField
# 后期想实现如下格式返回的数据
{name:书名,price:价格,publish:{name:出版社名,addr:地址},authors:[{},{}]}
# 方式一:在表模型中定义方法
在模型表中定义方法 需要模型表和序列化类一起配合使用
# 序列化类里面的字段
# 出版社对象详细信息
publish_detail = serializers.DictField()
# 作者详细信息
authors_detail = serializers.ListField()
# 模型层中定义
@property
def publish_detail(self):
# 这里返回列表还是字段,取决于序列化类的字段定义的是 ListField 还是 DictField
return {"publish_name": self.publish.name, "publish_addr": self.publish.addr}
@property
def authors_detail(self):
items = []
# 因为这里是 通过 manytomany创建的第三张表, authors 是一个对象,要拿全部数据要通过 all
for author in self.authors.all():
items.append(model_to_dict(author))
通过此方法返回自定义字段,而不通过source实现
思考?这样操作有什么好处?
- 更加灵活的定制要返回的数据,隐藏一些敏感信息,或者增加一些信息
- 比如身份证号、银行卡、电话号码、地址等,隐藏部分敏感信息。
# 自定义序列化类
book_name = serializers.CharField()
# 模型层
@property
def book_name(self):
return '可爱的作者:' + self.name
代码
# 视图层
from django.shortcuts import render, HttpResponse
from django.views import View
from .serializers import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from django.forms.models import model_to_dict
class BookView(APIView):
def get(self, request):
book_obj = Book.objects.all()
serializer = BookSerializer(instance=book_obj, many=True)
return Response({"code": 100, "msg": "查询成功!", "results": serializer.data})
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"code": 100, "msg": "新增成功!"})
else:
return Response({"code": 100, "msg": serializer.errors})
# 模型层
from django.db import models
from django.forms.models import model_to_dict
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name="书籍名称")
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="书籍价格")
publish_date = models.DateField(verbose_name="出版日期")
# 这里是一个对象,所以可以直接通过 . 获取数据
publish = models.ForeignKey(to='publish', on_delete=models.CASCADE, verbose_name="外键,关联出版社,设置级联更新和级联删除")
# 数据库中不会有这个字段。
# 会生成第三张表 authors
authors = models.ManyToManyField(to="Authors")
class Meta:
db_table = "book"
# 如果要在序列化类使用字段,那么一定要保证模型层中有
# 所以这里包装成数据属性,这样序列化类可以直接使用,然后渲染给前端
@property
def publish_detail(self):
# 这里返回列表还是字段,取决于序列化类的字段定义的是 ListField 还是 DictField
return {"publish_name": self.publish.name, "publish_addr": self.publish.addr}
@property
def authors_detail(self):
items = []
# 因为这里是 通过 manytomany创建的第三张表, authors 是一个对象,要拿全部数据要通过 all
for author in self.authors.all():
items.append(model_to_dict(author))
return items
@property
def book_name(self):
return '经典书籍:' + self.name
class Publish(models.Model):
name = models.CharField(max_length=32, verbose_name='出版社名称')
addr = models.CharField(max_length=32, verbose_name="出版社地址")
class Meta:
db_table = "publish"
def __str__(self):
return self.name
class Authors(models.Model):
name = models.CharField(max_length=32, verbose_name='作者姓名')
age = models.IntegerField(verbose_name="作者的年龄")
class Meta:
db_table = "authors"
# 序列化类
from rest_framework import serializers
from datetime import datetime
from rest_framework.exceptions import ValidationError
from .models import Book
def check_name(book_name):
if "sb" in book_name:
raise ValidationError("包含不合法的输入 sb ")
else:
return book_name
class BookSerializer(serializers.Serializer):
# book_name = serializers.CharField(source="name", max_length=8, validators=[check_name])
# 尝试通过 在模型层定义方法 返回 book_name 不适用 source
book_name = serializers.CharField()
price = serializers.DecimalField(max_digits=5, decimal_places=2, error_messages={"max_digits": "最少5为"})
# 这里的allow_null如果设置了,一般需要结合默认值一起去设置才会生效
publish_date = serializers.DateField(allow_null=True, default=datetime.now)
# publish = serializers.CharField(source="publish.id") 这里不需要指定publish.id 不然会报错
# 如果这样写,会把publish的打印的样子给前端,models可以配合__str__打印描述信息,但是不建议这样
# 因为是一个对象,我需要里面更多的数据,而不是单单打印一个publish.name (models)设置的__str__
# publish = serializers.CharField()
publish = serializers.CharField()
# 出版社对象详细信息
publish_detail = serializers.DictField()
# 作者详细信息
# authors_detail = serializers.ListField()
# 可以跨表查询
#
publish_id = serializers.CharField(source="publish.id")
# publish_id = serializers.IntegerField(source="publish.id")
publish_name = serializers.CharField(source="publish.name")
publish_addr = serializers.CharField(source='publish.addr')
def create(self, validated_data):
publish = validated_data.pop("publish")
book = Book.objects.create(**validated_data, publish_id=publish)
return book
通过 SerializerMethodField 去定制 只用到序列化类和模型表没有关系
- 一定要配合一个方法 get_字段名
- 其他的和上面的ListField/DictField一样
# 尝试通过 SerializerMethodField 返回book_name
book_name = serializers.SerializerMethodField()
# 出版社对象详细信息
publish_detail = serializers.SerializerMethodField()
# 作者详细信息
authors_detail = serializers.SerializerMethodField()
def get_publish_detail(self, obj):
# 这个obj 就是当前序列化的 book 对象
return model_to_dict(obj.publish)
def get_authors_detail(self, obj):
# 因为是第三张表 不要忘记 all 拿取所有
return [model_to_dict(author) for author in obj.authors.all()]
# 也可以自己去定制
def get_book_name(self, obj):
return "必读经典书籍:" + obj.name
子序列化
- 定义一个类,继承
serializers.Serializer
- 通过名称指定即可:publish_detail = PublisuSerializer(source="publish")
- 如果是多条需要指定many=True
- 多条,一般是多对多的第三张表
- 单条,一般是一对多的第三张表
# 定义子序列化类,一般是有外键关系的 多对多 一对多
class PublisuSerializer(serializers.Serializer):
# 这下面就写要序列化给前端的字段
id = serializers.IntegerField() # 出版社ID
name = serializers.CharField() # 出版社名称
addr = serializers.CharField() # 出版社地址
class AuthorsSerializer(serializers.Serializer):
id = serializers.IntegerField() # 作者ID
name = serializers.CharField() # 作者姓名
small_age = serializers.IntegerField(source="age") # 作者年龄(重命名为small_age)
class BookSerializer(serializers.Serializer):
# 子序列化 展示出版社详细信息
# 一样可以通过source去指定名称
# 拿着 PublisuSerializer 类里面的格式去做序列化
publish_detail = PublisuSerializer(source="publish") # 出版社详细信息序列化器
# 子序列化,展示作者详细信息
authors_detail = AuthorsSerializer(many=True, source="authors") # 作者详细信息序列化器(多个)
反序列化保存 即 保存前端校验通过的数据
read_only和write_only
- 后端传递给前端的数据,使用read_only
- 后端反序列化前端传过来的数据,写入到数据库里面,使用write_only
- read_only和write_only一起使用
- 如果是read_only的字段,不填写前端不会报错必填。
- 反序列化的字段,可以随意命名,跟表字段没关系,但是后续保存和修改要对应好才行。
# 注意:
1 read_only write_only 控制序列化类中某些字段,只用来序列化或反序列化
2 重写updata和create,保存逻辑,我们自己写
3 视图类中 serializer = BookSerializer(instance=pk, data=request.data)
-后续在序列化类中的update中def update(self, instance, validated_data):
-instance就是当时给的
代码
# 序列化类.py
from rest_framework import serializers
from datetime import datetime
from rest_framework.exceptions import ValidationError
from .models import Book
from django.forms.models import model_to_dict
# 定义子序列化类,一般是有外键关系的 多对多 一对多
class PublisuSerializer(serializers.Serializer):
# 这下面就写要序列化给前端的字段
id = serializers.IntegerField()
name = serializers.CharField()
addr = serializers.CharField()
class AuthorsSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
small_age = serializers.IntegerField(source="age")
class BookSerializer(serializers.Serializer):
book_name = serializers.CharField(source="name")
price = serializers.DecimalField(max_digits=5, decimal_places=2)
publish_date = serializers.DateField(allow_null=True, default=datetime.now)
# 子序列化 展示出版社详细信息
# 一样可以通过source去指定名称
publish_detail = PublisuSerializer(source="publish", read_only=True)
# 子序列化,展示作者详细信息
authors_detail = AuthorsSerializer(many=True, source="authors", read_only=True)
# 反序列化 保存数据
# 这里的publish 是models的一对多外键 前端传入数字 会自动根出版社的id做对应
publish = serializers.IntegerField(write_only=True)
# 多对多 是列表 所以要使用ListField
authors = serializers.ListField(write_only=True)
def create(self, validated_data):
# {'name': '天龙八部', 'price': Decimal('89.00'), 'publish_date': datetime.datetime(2024, 4, 12, 22, 18, 5, 785495), 'publish': 1, 'authors': [1]}
publish_id = validated_data.pop("publish")
authors = validated_data.pop("authors")
# 先获取对象
book = Book.objects.create(**validated_data, publish_id=publish_id)
# 向中间表插入数据
book.authors.add(*authors)
return book
# 更新1 前端传入的是 pk
def update(self, instance, validated_data):
publish_id = validated_data.pop("publish")
authors = validated_data.pop("authors")
# 下面的方法会报错 因为uodate返回的并不是qs对象
# book = Book.objects.filter(pk=instance).update(**validated_data, publish_id=publish_id)
# book.authors.set(*authors) # AttributeError: 'int' object has no attribute 'authors'
book_qs = Book.objects.filter(pk=instance) # 找到这个qs对象
book_qs.update(**validated_data, publish_id=publish_id) # 使用qs去更新
obj = book_qs.first()
obj.authors.set(authors)
# 更新2 前端传入的是对象
# def update(self, instance, validated_data):
# publish_id = validated_data.pop("publish")
# authors = validated_data.pop("authors")
# for attr, value in validated_data.items():
# setattr(instance, attr, value)
# instance.publish_id = publish_id
# instance.authors.set(authors)
# instance.save()
# return instance
# views.py
from django.shortcuts import render, HttpResponse, get_object_or_404
from django.views import View
from rest_framework import status
from .serializers import BookSerializer
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from django.forms.models import model_to_dict
class BookView(APIView):
def get(self, request):
book_obj = Book.objects.all()
serializer = BookSerializer(instance=book_obj, many=True)
return Response({"code": 100, "msg": "查询成功!", "results": serializer.data})
# 新增一本书
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"code": 100, "msg": "新增成功!"})
else:
return Response({"code": 100, "msg": serializer.errors})
class BookDetailView(APIView):
def get(self, request, pk):
obj = Book.objects.filter(pk=pk).first()
serializer = BookSerializer(instance=obj)
return Response({"code": 100, "msg": serializer.data})
def delete(self, request, pk):
instance = get_object_or_404(Book, pk=pk)
instance.delete()
return Response({"code": 100, "msg": "删除成功!"}, status=status.HTTP_204_NO_CONTENT)
def put(self, request, pk):
# obj = Book.objects.filter(pk=pk).first() 如果要修改,因为这里是多表关联,所以可以不用传入对象,传入pk操作更方便
# 传入的instance是什么,到了update中,就是什么。
serializer = BookSerializer(instance=pk, data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"code": 100, "msg": "修改成功!"})
else:
return Response({"code": 100, "msg": serializer.errors})
ModelSerializer的使用 重要!
- 新增和更新,不需要再写了,不过字段要一一对应,不能修改,默认模型表定义的什么字段,序列化类里面就要什么字段,(看write_only)
- 不过如果是新增或者修改,instance不能传入pk了,只能传入对象。
- 局部钩子和全局钩子跟之前一样(注意层级),不要写到Meta里面去了,需要和Meta同级。
# views.py
def put(self, request, pk):
obj = Book.objects.filter(pk=pk).first() # 如果使用serializers.ModelSerializer 这里只能传入一个对象
serializer = BookSerializer(instance=obj, data=request.data)
if serializer.is_valid():
serializer.save()
return Response({"code": 100, "msg": "修改成功!"})
else:
return Response({"code": 100, "msg": serializer.errors})
# models.py
from django.db import models
from django.forms.models import model_to_dict
class Book(models.Model):
name = models.CharField(max_length=32, verbose_name="书籍名称")
price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="书籍价格")
publish_date = models.DateField(verbose_name="出版日期")
# 这里是一个对象,所以可以直接通过 . 获取数据
publish = models.ForeignKey(to='publish', on_delete=models.CASCADE, verbose_name="外键,关联出版社,设置级联更新和级联删除")
# 数据库中不会有这个字段。
# 会生成第三张表 authors
authors = models.ManyToManyField(to="Authors")
class Meta:
db_table = "book"
class Publish(models.Model):
name = models.CharField(max_length=32, verbose_name='出版社名称')
addr = models.CharField(max_length=32, verbose_name="出版社地址")
class Meta:
db_table = "publish"
def __str__(self):
return self.name
class Authors(models.Model):
name = models.CharField(max_length=32, verbose_name='作者姓名')
age = models.IntegerField(verbose_name="作者的年龄")
class Meta:
db_table = "authors"
# 自定义序列化类.py
from rest_framework import serializers
from datetime import datetime
from rest_framework.exceptions import ValidationError
from .models import Book
from django.forms.models import model_to_dict
# 定义子序列化类,一般是有外键关系的 多对多 一对多
class PublisuSerializer(serializers.Serializer):
# 这下面就写要序列化给前端的字段
id = serializers.IntegerField()
name = serializers.CharField()
addr = serializers.CharField()
class AuthorsSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
small_age = serializers.IntegerField(source="age")
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book # 要关联的表
fields = "__all__" # 要关联哪些字段
# 如果要关联指定的字段 加入到一个列表里面 列表里面放进去字段
# 这个字段需要用引号包裹起来
# fields = ["name", "publish" ...] #
# 做控制,有的使用序列化,有的使用反序列化
extra_kwargs = {
"publish": {"write_only": True},
"name": {"max_length": 8}, # 控制参数
"authors": {"write_only": True}
}
# 如果有自己额外的子序列化字段,需要class Meta 同级新增, 不要写到 Meta 内部了!!!
publish_detail = PublisuSerializer(read_only=True, source="publish")
authors_detail = AuthorsSerializer(read_only=True, source="authors", many=True)
本文作者:小满三岁啦
本文链接:https://www.cnblogs.com/ccsvip/p/18132402
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步