[Django]-Django rest framework abstract class serializer

21👍

I think the following approach is more cleaner. You can set “abstract” field to true for the base serializer and add your common logic for all child serializers.

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

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

And then create child serializers and use them for data manipulation.

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


class CompanyTypeSerializer(TypeBaseSerializer):
    class Meta:
        model = CompanyType
        fields = ('id', 'name')

Now you can use the both of these serializers normally for every model.

But if you really want to have one serializers for both the models, then create a container model and a serializer for him too. That is much cleaner 🙂

👤adkl

18👍

As already mentioned in Sebastian Wozny’s answer, you can’t use a ModelSerializer with an abstract base model.

Also, there is nothing such as an abstract Serializer, as some other answers have suggested. So setting abstract = True on the Meta class of a serializer will not work.

However you need not use use a ModelSerializer as your base/parent serializer. You can use a Serializer and then take advantage of Django’s multiple inheritance. Here is how it works:

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']

So now since the fields name and id are declared on the parent class (TypeBaseSerializer), they will be available on PersonTypeSerializer and since this is a child class of ModelSerializer those fields will be populated from the model instance.

You can also use SerializerMethodField on the TypeBaseSerializer, even though it is not a 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

17👍

You can’t use a ModelSerializer with an abstract base model.
From restframework.serializers:

if model_meta.is_abstract_model(self.Meta.model):
        raise ValueError(
            'Cannot use ModelSerializer with Abstract Models.'
        )

I wrote a serializer_factory function for a similar problem:

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

You can then use the factory to produce serializers as needed:

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

Now instanciate different subclass serializers:

persontypeserializer = typebase_serializer_factory(PersonType)
companytypeserializer = typebase_serializer_factory(CompanyType)

9👍

Just iterating a bit over @adki’s answer:

  1. it is possible to skip model for TypeBaseSerializer;
  2. derived serializers can refer to TypeBaseSerializer.Meta, so you would change them in a single place.
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

Leave a comment