I have some models like these:
class TypeBase(models.Model):
name = models.CharField(max_length=20)
class Meta:
abstract=True
class PersonType(T
Just iterating a bit over @adki's answer:
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
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
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)
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 :)