I have some models like these:
class TypeBase(models.Model):
name = models.CharField(max_length=20)
class Meta:
abstract=True
class PersonType(TypeBase):
pass
class CompanyType(TypeBase):
pass
Having this, I want to create just one serializer that holds all these field types (serialization, deserialization, update and save).
To be more specific, I want only one serializer (TypeBaseSerializer) that print the Dropdown on the UI, serialize the json response, deserialize it on post and save it for all my based types.
Something like this:
class TypeBaseSerializer(serializers.Serializer):
class Meta:
model = TypeBase
fields = ('id', 'name')
Is it possible?
Serializers in Django REST Framework are responsible for converting objects into data types understandable by javascript and front-end frameworks. Serializers also provide deserialization, allowing parsed data to be converted back into complex types, after first validating the incoming data.
To serialize a queryset or list of objects instead of a single object instance, you should pass the many=True flag when instantiating the serializer. You can then pass a queryset or list of objects to be serialized.
In function based views we can pass extra context to serializer with "context" parameter with a dictionary. To access the extra context data inside the serializer we can simply access it with "self. context". From example, to get "exclude_email_list" we just used code 'exclude_email_list = self.
It serves the same point as using a Meta class object inside a Django model class, or a form class, etc. It's a standard pattern to attach configuration (metadata) to a set of fields that make up the model, form, or in this case, serializer.
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 :)
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)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With