Serializers 序列化组件
什么要用序列化组件 :
在我们做前后端分离的项目时候, 我们前后端交互一般都选择JSON数据格式, JSON是一个轻量级的数据交互格式.
那么我们给前端数据的时候都要转成JSON格式, 那就需要对我们从数据库拿到的数据进行序列化.
Django的序列化方法 :
class BooksView(View): def get(self, request): book_list = Book.objects.values("id", "title", "chapter", "pub_time", "publisher") book_list = list(book_list) # 如果我们需要取外键关联的字段信息 需要循环获取外键 再去数据库查然后拼接成我们想要的 ret = [] for book in book_list: pub_dict = {} pub_obj = Publish.objects.filter(pk=book["publisher"]).first() pub_dict["id"] = pub_obj.pk pub_dict["title"] = pub_obj.title book["publisher"] = pub_dict ret.append(book) ret = json.dumps(book_list, ensure_ascii=False, cls=MyJson) return HttpResponse(ret) # json.JSONEncoder.default() # 解决json不能序列化时间字段的问题 class MyJson(json.JSONEncoder): def default(self, field): if isinstance(field, datetime.datetime): return field.strftime('%Y-%m-%d %H:%M:%S') elif isinstance(field, datetime.date): return field.strftime('%Y-%m-%d') else: return json.JSONEncoder.default(self, field)
from django.core import serializers # 能够得到我们要的效果 结构有点复杂 class BooksView(View): def get(self, request): book_list = Book.objects.all() ret = serializers.serialize("json", book_list) return HttpResponse(ret)
DRF序列化方法 :
想要使用DRF的序列化, 就要遵循人家框架的一些标准 :
-Django 中CBV继承类时View, 现在DRF要继承APIView
-Django 中返回的时候我们用HTTPResponse, JsonResponse, reder, 而DRF我们使用Response来接受.
序列化 :
class Book(models.Model): title = models.CharField(max_length=32) CHIOCES = ((1, "python"), (2, "linux"), (3, "go")) category = models.IntegerField(choices=CHIOCES) pub_time = models.DateField() publisher = models.ForeignKey(to="Publisher") author = models.ManyToManyField(to="Author")
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # required 为False时, 反序列化不做校验 title = serializers.CharField(max_length=32, validators=[my_validate]) pub_time = serializers.DateField() CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python")) category = serializers.CharField(choices=CHOICES) publisher = PublisherSerializer(read_only=True) # 多对多有many参数 author = AuthorSerializer(many=True, read_only=True)
from rest_framework.views import APIView from app01 import models from .serializers import BookSerializer from rest_framework.response import Response class BookView(APIView): def get(self, request): book_queryset = models.Book.objects.all() # 用序列化器进行序列化 ser_obj = BookSerializer(book_queryset, many=True) return Response(ser_obj.data)
外键关系的序列化 :
from rest_framework import serializers from app01 import models class PublisherSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=32) class AuthorSerializer(serializers.Serializer): id = serializers.IntegerField() name = serializers.CharField(max_length=32) class BookSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32) CHOICES = ((1, "Linux"), (2, "Django"), (3, "Python")) chapter = serializers.ChoiceField(choices=CHOICES, ) pub_time = serializers.DateField() publisher = PublisherSerializer(read_only=True) # 多对多字段有many=True参数 author= AuthorSerializer(many=True, read_only=True)
反序列化 :
当前端给我们发送post请求时候, 前端给我们传来的数据, 我们要经过一些校验然后保存到数据库. 这些校验以及爆粗工作, DRF的serializer也给我们提供了一些方法了.
首先, 我们要写反序列化需要使用的一些字段, 有些字段(外键, 多对多, choices)需要和序列化字段分开.
serializer提供了 .is_valid() 和 .save()方法
from django.db import models class Book(models.Model): title = models.CharField(max_length=32) CHIOCES = ((1, "python"), (2, "linux"), (3, "go")) category = models.IntegerField(choices=CHIOCES) pub_time = models.DateField() publisher = models.ForeignKey(to="Publisher") author = models.ManyToManyField(to="Author") class Publisher(models.Model): title = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32)
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # required 为False时, 反序列化不做校验 title = serializers.CharField(max_length=32, validators=[my_validate]) pub_time = serializers.DateField() category = serializers.CharField(source="get_category_display", read_only=True) # 自定义一个字段只用来反序列化接收使用 post_category = serializers.IntegerField(write_only=True) publisher = PublisherSerializer(read_only=True) publisher_id = serializers.IntegerField(write_only=True) # 多对多有many参数 author = AuthorSerializer(many=True, read_only=True) author_list = serializers.ListField(write_only=True) # 新增数据要重写的create方法 def create(self, validated_data): # validated_data是验证通过的数据 # 通过ORM操作给Book表增加数据 # 添加除多对多字段的所有字段 book_obj = models.Book.objects.create( title=validated_data["title"], pub_time=validated_data["pub_time"], category=validated_data["post_category"], publisher_id=validated_data["publisher_id"], ) # 添加多对多字段 book_obj.author.add(*validated_data["author_list"]) return book_obj
class BookView(APIView): def get(self, request): book_queryset = models.Book.objects.all() # 用序列化器进行序列化 ser_obj = BookSerializer(book_queryset, many=True) return Response(ser_obj.data) def post(self, request): # 接收前端传过来的数据 book_obj = request.data # 对前端传过来的数据使用自定义序列化方法进行校验(是否合法等) ser_obj = BookSerializer(data=book_obj) # 如果校验通过做些什么 if ser_obj.is_valid(): ser_obj.save() # validated_data是校验通过之后的数据 return Response(ser_obj.validated_data) # 验证不通过返回错误信息 return Response(ser_obj.errors)
当前端给我们发送put或者delete请求的时候, 前端给我们用户需要更新的数据, 我们要对数据进行部分验证.
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # required 为False时, 反序列化不做校验 title = serializers.CharField(max_length=32, validators=[my_validate]) pub_time = serializers.DateField() category = serializers.CharField(source="get_category_display", read_only=True) # 自定义一个字段只用来反序列化接收使用 post_category = serializers.IntegerField(write_only=True) publisher = PublisherSerializer(read_only=True) publisher_id = serializers.IntegerField(write_only=True) # 多对多有many参数 author = AuthorSerializer(many=True, read_only=True) author_list = serializers.ListField(write_only=True) # 新增数据要重写的create方法 def create(self, validated_data): # validated_data是验证通过的数据 # 通过ORM操作给Book表增加数据 # 添加除多对多字段的所有字段 book_obj = models.Book.objects.create( title=validated_data["title"], pub_time=validated_data["pub_time"], category=validated_data["post_category"], publisher_id=validated_data["publisher_id"], ) # 添加多对多字段 book_obj.author.add(*validated_data["author_list"]) return book_obj # 更新数据要重写update方法 def update(self, instance, validated_data): # instance 是要更新的对象 # 对除多对多字段以外的字段进行更新, 并设置当前已存在的数据为默认值 instance.title = validated_data.get("title", instance.title) instance.pub_time = validated_data.get("pub_time", instance.pub_time) instance.category = validated_data.get("post_category", instance.category) instance.publisher_id = validated_data.get("publisher_id", instance.publisher_id) # 判断前端传过来的数据是否含有author_list字段, 如果有则更新, 没有就不变动 if validated_data.get("author_list"): instance.author.set(validated_data["author_list"]) instance.save() return instance
from rest_framework.views import APIView from app01 import models from .serializers import BookSerializer from rest_framework.response import Response class BookEditView(APIView): def get(self, request, edit_id): book_obj = models.Book.objects.filter(id=edit_id).first() ser_obj = BookSerializer(book_obj) return Response(ser_obj.data) def put(self, request, edit_id): book_obj = models.Book.objects.filter(id=edit_id).first() ser_obj = BookSerializer(instance=book_obj, data=request.data, partial=True) if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.validated_data) return Response(ser_obj.errors) def delete(self, request, edit_id): book_obj = models.Book.objects.filter(id=edit_id).first() if not book_obj: return Response("删除对象不存在!") book_obj.delete() return Response("删除成功了呢!")
验证 :
如果我们需要对一些字段进行自定义的验证, DRF也给我们提供了钩子方法.
def my_validate(value): if "敏感信息" in value.lower(): raise serializers.ValidationError("存在敏感词汇!!!") class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # required 为False时, 反序列化不做校验 title = serializers.CharField(max_length=32, validators=[my_validate])
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(read_only=True) title = serializers.CharField(max_length=32) # 省略了一些字段 跟上面代码里一样的 # 。。。。。 def validate_title(self, value): if "python" not in value.lower(): raise serializers.ValidationError("标题必须含有Python") return value
class BookSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # required 为False时, 反序列化不做校验 title = serializers.CharField(max_length=32, validators=[my_validate]) pub_time = serializers.DateField() category = serializers.CharField(source="get_category_display", read_only=True) # 自定义一个字段只用来反序列化接收使用 post_category = serializers.IntegerField(write_only=True) publisher = PublisherSerializer(read_only=True) publisher_id = serializers.IntegerField(write_only=True) # 多对多有many参数 author = AuthorSerializer(many=True, read_only=True) author_list = serializers.ListField(write_only=True) # 对前端传过来的数据进行条件控制 def validate(self, attrs): # 相当于钩子函数 # attrs是一个字典, 含有传过来的所有字段 if "python" in attrs["title"].lower() and attrs["post_category"] == 1: return attrs else: raise serializers.ValidationError("分类或标题不匹配")
ModelSerizlizer :
当我们清楚了Srrializer的用法之后, 会发现所有的序列化跟我们的模型都紧密相关.
既然如此, DRF也给我们提供了跟模型紧密相关的序列化器: ModelSerializer
此序列化器会根据模型自动生成一组字段
并且简单的默认实现了 .update()以及 .create()方法
定义一个ModelSerializer序列化器 :
class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # fields = ["id", "title", "pub_time"] # exclude = ["user"] # 分别是所有字段 包含某些字段 排除某些字段
外键关系的序列化 :
※. 当序列化类MATE中定义了depth时, 这个序列化类中引用字段(外键)则自动变为只读.
class BookSerializer(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" # fields = ["id", "title", "pub_time"] # exclude = ["user"] # 分别是所有字段 包含某些字段 排除某些字段 depth = 1 # depth 代表找嵌套关系的第几层
自定义字段 :
可以通过声明字段的防水来覆盖默认字段, 来进行自定制
比如model中的choices字段, 默认显示的是选择的key, 而要展示给用户的是value
class BookSerializer(serializers.ModelSerializer): chapter = serializers.CharField(source="get_chapter_display", read_only=True) class Meta: model = Book fields = "__all__" # fields = ["id", "title", "pub_time"] # exclude = ["user"] # 分别是所有字段 包含某些字段 排除某些字段 depth = 1
Meta中的其他关键字参数 :
class BookSerializer(serializers.ModelSerializer): """ 继承ModelSerializer方法不需要在定义一个用来反序列化的字段 """ # 以下方法为自定义外键, 多对多, choices字段的显示内容 # 根据自己方法里构建的数据返回给指定的字段, 钩子函数返回什么就展示什么 publisher_info = serializers.SerializerMethodField(read_only=True) author_info = serializers.SerializerMethodField(read_only=True) category_info = serializers.SerializerMethodField(read_only=True) # SerializerMethodField提供的钩子函数, get_使用钩子函数的字段名 # 此钩子函数的返回值会返回给SerializerMethodField方法 # obj参数为调用序列化方法的QuerySet中的每个对象 def get_publisher_info(self, obj): # publisher_query为每个序列化对象所关联的外键对象 publisher_query = obj.publisher return {"id": publisher_query.id, "title": publisher_query.title} def get_author_info(self, obj): # author_query为每个要序列化对象所关联的多对多的全部对象 author_query = obj.author.all() return [{"id": author.id, "name": author.name} for author in author_query] # choices字段, 将要序列化的对象直接调用一次display方法, 使其显示具体内容即可 def get_category_info(self, obj): return obj.get_category_display() class Meta: model = models.Book fields = "__all__" # "depth解释 ↓↓↓" # 会查找一层外键关联的对象的所有(划重点)字段内容 # 但会使这些所有的外键关系变成read_only = True, 反序列化外键关系会丢失 # 一般情况下不常用 # depth = 1 # 加自定义操作的方法 # 使publisher, author字段添加一个write_only参数, 是其只能进行反序列化使用 extra_kwargs = {"publisher": {"write_only": True}, "author": {"write_only": True}, "category": {"write_only": True}}