django rest框架抽象类序列化器

Tia*_*qui 11 python django abstract-class django-rest-framework

我有一些这样的模型:

class TypeBase(models.Model):
    name = models.CharField(max_length=20)
    class Meta:
        abstract=True

class PersonType(TypeBase):
    pass

class CompanyType(TypeBase):
    pass
Run Code Online (Sandbox Code Playgroud)

有了这个,我想创建一个包含所有这些字段类型的序列化器(序列化,反序列化,更新和保存).

更具体地说,我只想要一个序列化程序(TypeBaseSerializer)在UI上打印Dropdown,序列化json响应,在post上反序列化并保存它用于所有基于类型.

像这样的东西:

class TypeBaseSerializer(serializers.Serializer):
    class Meta:
        model = TypeBase
        fields = ('id', 'name')
Run Code Online (Sandbox Code Playgroud)

可能吗?

Seb*_*zny 12

不能将 a ModelSerializer与抽象基础模型一起使用.来自restframework.serializers:

if model_meta.is_abstract_model(self.Meta.model):
        raise ValueError(
            'Cannot use ModelSerializer with Abstract Models.'
        )
Run Code Online (Sandbox Code Playgroud)

我为类似的问题写了一个serializer_factory函数:

from collections import OrderedDict
from restframework.serializers import ModelSerializer
def serializer_factory(mdl, fields=None, **kwargss):
""" Generalized serializer factory to increase DRYness of code.

:param mdl: The model class that should be instanciated
:param fields: the fields that should be exclusively present on the serializer
:param kwargss: optional additional field specifications
:return: An awesome serializer
"""

    def _get_declared_fields(attrs):
        fields = [(field_name, attrs.pop(field_name))
                  for field_name, obj in list(attrs.items())
                  if isinstance(obj, Field)]
        fields.sort(key=lambda x: x[1]._creation_counter)
        return OrderedDict(fields)

    # Create an object that will look like a base serializer
    class Base(object):
        pass

    Base._declared_fields = _get_declared_fields(kwargss)

    class MySerializer(Base, ModelSerializer):
        class Meta:
            model = mdl

        if fields:
            setattr(Meta, "fields", fields)

    return MySerializer
Run Code Online (Sandbox Code Playgroud)

然后,您可以根据需要使用工厂生成序列化程序:

def typebase_serializer_factory(mdl):
    myserializer = serializer_factory(
        mdl,fields=["id","name"],
        #owner=HiddenField(default=CurrentUserDefault()),#Optional additional configuration for subclasses 
      )
    return myserializer
Run Code Online (Sandbox Code Playgroud)

现在实例化不同的子类序列化器:

persontypeserializer = typebase_serializer_factory(PersonType)
companytypeserializer = typebase_serializer_factory(CompanyType)
Run Code Online (Sandbox Code Playgroud)


adk*_*dkl 7

我认为以下方法更清洁。您可以将基本串行器的“抽象”字段设置为true,并为所有子串行器添加通用逻辑。

class TypeBaseSerializer(serializers.ModelSerializer):
    class Meta:
        model = TypeBase
        fields = ('id', 'name')
        abstract = True

    def func(...):
    # ... some logic
Run Code Online (Sandbox Code Playgroud)

然后创建子序列化器并将其用于数据处理。

class PersonTypeSerializer(TypeBaseSerializer):
    class Meta:
        model = PersonType
        fields = ('id', 'name')


class CompanyTypeSerializer(TypeBaseSerializer):
    class Meta:
        model = CompanyType
        fields = ('id', 'name')
Run Code Online (Sandbox Code Playgroud)

现在,您可以正常使用每种型号的这两种序列化器。

但是,如果您真的想要两个模型都有一个序列化器,那么也可以为他创建一个容器模型和一个序列化器。那更干净了:)

  • @GunnerFan,你是对的,“abstract”不是可设置的属性。正确的方法是仅使用serializer.Serializer作为基类,这实际上最终会成为一个mixin,并使用serializers.ModelSerializer作为派生类 (2认同)

Gun*_*Fan 6

正如在Sebastian Wozny 的回答中已经提到,您不能将 ModelSerializer 与抽象基本模型一起使用。

此外,正如其他一些答案所建议的那样,没有诸如抽象序列化程序之类的东西。因此abstract = True在序列化程序的 Meta 类上设置将不起作用

但是,您不需要使用 aModelSerializer作为您的基本/父序列化程序。您可以使用 aSerializer然后利用 Django 的多重继承。下面是它的工作原理:

class TypeBaseSerializer(serializers.Serializer):
    # Need to re-declare fields since this is not a ModelSerializer
    name = serializers.CharField()
    id = serializers.CharField()

    class Meta:
        fields = ['id', 'name']

    def someFunction(self):
        #... will be available on child classes ...
        pass

class PersonTypeSerializer(TypeBaseSerializer, serializers.ModelSerializer):

    class Meta:
        model = PersonType
        fields = TypeBaseSerializer.Meta.fields + ['another_field']


class CompanyTypeSerializer(TypeBaseSerializer, serializers.ModelSerializer):

    class Meta:
        model = CompanyType
        fields = TypeBaseSerializer.Meta.fields + ['some_other_field']
Run Code Online (Sandbox Code Playgroud)

所以现在因为字段nameid是在父类(TypeBaseSerializer)上声明的,它们将可用,PersonTypeSerializer并且因为这是ModelSerializer这些字段的子类,将从模型实例中填充。

您也可以SerializerMethodField在 上使用TypeBaseSerializer,即使它不是 ModelSerializer。

class TypeBaseSerializer(serializers.Serializer):
    # you will have to re-declare fields here since this is not a ModelSerializer
    name = serializers.CharField()
    id = serializers.CharField()
    other_field = serializers.SerializerMethodField()

    class Meta:
        fields = ['id', 'name', 'other_field']

    def get_other_field(self, instance):
        # will be available on child classes, which are children of ModelSerializers
        return instance.other_field
Run Code Online (Sandbox Code Playgroud)


Kos*_*tyn 5

只是迭代@adki的答案:

  1. 可以跳过TypeBaseSerializer的模型;
  2. 派生的序列化程序可以引用TypeBaseSerializer.meta.fields,因此您可以在一个位置进行更改。
class TypeBaseSerializer(serializers.Serializer):
    class Meta:
        fields = ('id', 'name', 'created')
        abstract = True

    def func(...):
    # ... some logic

class PersonTypeSerializer(TypeBaseSerializer):
    class Meta:
        model = PersonType
        fields = TypeBaseSerializer.Meta.fields + ('age', 'date_of_birth')

class CompanyTypeSerializer(TypeBaseSerializer):
    class Meta:
        model = CompanyType
        fields = TypeBaseSerializer.Meta.fields
Run Code Online (Sandbox Code Playgroud)

  • 据我所见,类“ serializers.Serializer”没有属性“ meta”。但是,它具有“ Meta”属性。所以我想应该是`TypeBaseSerializer.Meta.fields`而不是`TypeBaseSerializer.meta.fields`。 (3认同)