现状
最近的项目是用DRF框架实现后端的接口,视图果断使用modelviewset,而也不想写curd对应好几个serializer,希望一个serializer搞定所有,于是就涉及到需要动态指定序列化和反序列化字段的问题(比如list和detail的需要的字段不一样,list往往只要少量几个关键字段做展示,detail需要更多字段),找了下网上第三方的包,没有特别合适的,于是开启了自己造轮子之路
功能
1.接口默认可以取到Serializer.Meta.fields里所有的字段,
2.查询接口(detail,list)可以传参fields动态指定需要序列化返回的字段
3.更新接口(update)可以传参fields动态指定允许反序列化保存的字段
4.可以在Serializer.Meta中配置list_fields属性,list接口用这个属性作为序列化字段,没配这个属性用原来的Meta.fields序列化;(如果同时传参fields进来做序列化,优先传参指定的fields)
DynamicFieldsModelSerializer代码
from rest_framwork import serializers
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
"""支持动态指定字段的序列化器,传参fields,序列化和反序列化都支持"""
Meta: type
def __init__(self, *args, **kwargs):
"""支持字段动态生成的序列化器,从默认的Meta.fields中过滤,无关字段不查不序列化"""
fields = kwargs.pop('fields', None)
super().__init__(*args, **kwargs)
if fields is not None:
allow = set(fields)
existing = set(self.fields)
for f in existing - allow:
self.fields.pop(f)
def __new__(cls, *args, **kwargs):
"""list序列化时,首先使用传参的fields,默认用meta.list_fields作为序列化字段"""
if kwargs.pop('many', False):
fields = getattr(cls.Meta, 'list_fields', None)
if fields and 'fields' not in kwargs:
kwargs['fields'] = fields
return cls.many_init(*args, **kwargs)
return super().__new__(cls, *args, **kwargs)
调用举例
from django.db import models
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
from django.http.response import HttpResponse, JsonResponse
class Task(models.Model):
name = models.CharField(max_length=255, null=False)
desc = models.CharField(max_length=255, null=False)
status = models.CharField(max_length=255, null=False)
class TaskSerializer(DynamicFieldsModelSerializer):
class Meta:
model = Task
fields = ['id', 'name', 'desc', 'status'] # 全部字段放这里
list_fields = ['id', 'name'] # list用的字段,放这里
class TaskViewSet(ModelViewSet):
queryset = Task.objects
serializer_class = TaskSerializer
@action(methods=['post'], detial=False)
def close(self, request, *args, **kwargs):
"""关闭任务,只更新'status', 'desc'两个字段"""
task = self.get_object()
request.data['status'] = 'closed'
serializer = self.get_serializer(task, data=request.data, partial=True, fields=['status', 'desc'])
serializer.is_valid(raise_exception=True)
serializer.save()
return HttpResponse()
@action(methods=['get'], detial=False)
def my_detail(self, request, *args, **kwargs):
"""任务详情,只返回'id', 'name', 'desc' 3个字段"""
task = self.get_object()
serializer = self.get_serializer(task, fields=['id', 'name', 'desc'])
return JsonResponse(data=serializer.data)
# def list(self, request, *args, **kwargs):
# """
# 任务列表,只展示'id', 'name'两个字段
# 这里不需要单独实现,TaskSerializer.__new__方法会按照Meta中的list_fields树形动态指定序列化的字段
# """
# def retrieve(self, request, *args, **kwargs):
# """
# 任务详情,展示默认所有字段 'id', 'name', 'desc', 'status'
# """