Django Rest Framework – How to add custom field in ModelSerializer
Question:
I created a ModelSerializer
and want to add a custom field which is not part of my model.
I found a description to add extra fields here and I tried the following:
customField = CharField(source='my_field')
When I add this field and call my validate()
function then this field is not part of the attr
dict. attr
contains all model fields specified except the extra fields. So I cannot access this field in my overwritten validation, can I?
When I add this field to the field list like this:
class Meta:
model = Account
fields = ('myfield1', 'myfield2', 'customField')
then I get an error because customField
is not part of my model – what is correct because I want to add it just for this serializer.
Is there any way to add a custom field?
Answers:
You’re doing the right thing, except that CharField
(and the other typed fields) are for writable fields.
In this case you just want a simple read-only field, so instead just use:
customField = Field(source='get_absolute_url')
here answer for your question.
you should add to your model Account:
@property
def my_field(self):
return None
now you can use:
customField = CharField(source='my_field')
…for clarity, if you have a Model Method defined in the following way:
class MyModel(models.Model):
...
def model_method(self):
return "some_calculated_result"
You can add the result of calling said method to your serializer like so:
class MyModelSerializer(serializers.ModelSerializer):
model_method_field = serializers.CharField(source='model_method')
p.s. Since the custom field isn’t really a field in your model, you’ll usually want to make it read-only, like so:
class Meta:
model = MyModel
read_only_fields = (
'model_method_field',
)
With the last version of Django Rest Framework, you need to create a method in your model with the name of the field you want to add.
class Foo(models.Model):
. . .
def foo(self):
return 'stuff'
. . .
class FooSerializer(ModelSerializer):
foo = serializers.ReadOnlyField()
class Meta:
model = Foo
fields = ('foo',)
To show self.author.full_name
, I got an error with Field
. It worked with ReadOnlyField
:
class CommentSerializer(serializers.HyperlinkedModelSerializer):
author_name = ReadOnlyField(source="author.full_name")
class Meta:
model = Comment
fields = ('url', 'content', 'author_name', 'author')
In fact there a solution without touching at all the model. You can use SerializerMethodField
which allow you to plug any method to your serializer.
class FooSerializer(ModelSerializer):
foo = serializers.SerializerMethodField()
def get_foo(self, obj):
return "Foo id: %i" % obj.pk
I was looking for a solution for adding a writable custom field to a model serializer. I found this one, which has not been covered in the answers to this question.
It seems like you do indeed need to write your own simple Serializer.
class PassThroughSerializer(serializers.Field):
def to_representation(self, instance):
# This function is for the direction: Instance -> Dict
# If you only need this, use a ReadOnlyField, or SerializerField
return None
def to_internal_value(self, data):
# This function is for the direction: Dict -> Instance
# Here you can manipulate the data if you need to.
return data
Now you can use this Serializer to add custom fields to a ModelSerializer
class MyModelSerializer(serializers.ModelSerializer)
my_custom_field = PassThroughSerializer()
def create(self, validated_data):
# now the key 'my_custom_field' is available in validated_data
...
return instance
This also works, if the Model MyModel
actually has a property called my_custom_field
but you want to ignore its validators.
After reading all the answers here my conclusion is that it is impossible to do this cleanly. You have to play dirty and do something hadkish like creating a write_only field and then override the validate
and to_representation
methods. This is what worked for me:
class FooSerializer(ModelSerializer):
foo = CharField(write_only=True)
class Meta:
model = Foo
fields = ["foo", ...]
def validate(self, data):
foo = data.pop("foo", None)
# Do what you want with your value
return super().validate(data)
def to_representation(self, instance):
data = super().to_representation(instance)
data["foo"] = whatever_you_want
return data
I created a ModelSerializer
and want to add a custom field which is not part of my model.
I found a description to add extra fields here and I tried the following:
customField = CharField(source='my_field')
When I add this field and call my validate()
function then this field is not part of the attr
dict. attr
contains all model fields specified except the extra fields. So I cannot access this field in my overwritten validation, can I?
When I add this field to the field list like this:
class Meta:
model = Account
fields = ('myfield1', 'myfield2', 'customField')
then I get an error because customField
is not part of my model – what is correct because I want to add it just for this serializer.
Is there any way to add a custom field?
You’re doing the right thing, except that CharField
(and the other typed fields) are for writable fields.
In this case you just want a simple read-only field, so instead just use:
customField = Field(source='get_absolute_url')
here answer for your question.
you should add to your model Account:
@property
def my_field(self):
return None
now you can use:
customField = CharField(source='my_field')
…for clarity, if you have a Model Method defined in the following way:
class MyModel(models.Model):
...
def model_method(self):
return "some_calculated_result"
You can add the result of calling said method to your serializer like so:
class MyModelSerializer(serializers.ModelSerializer):
model_method_field = serializers.CharField(source='model_method')
p.s. Since the custom field isn’t really a field in your model, you’ll usually want to make it read-only, like so:
class Meta:
model = MyModel
read_only_fields = (
'model_method_field',
)
With the last version of Django Rest Framework, you need to create a method in your model with the name of the field you want to add.
class Foo(models.Model):
. . .
def foo(self):
return 'stuff'
. . .
class FooSerializer(ModelSerializer):
foo = serializers.ReadOnlyField()
class Meta:
model = Foo
fields = ('foo',)
To show self.author.full_name
, I got an error with Field
. It worked with ReadOnlyField
:
class CommentSerializer(serializers.HyperlinkedModelSerializer):
author_name = ReadOnlyField(source="author.full_name")
class Meta:
model = Comment
fields = ('url', 'content', 'author_name', 'author')
In fact there a solution without touching at all the model. You can use SerializerMethodField
which allow you to plug any method to your serializer.
class FooSerializer(ModelSerializer):
foo = serializers.SerializerMethodField()
def get_foo(self, obj):
return "Foo id: %i" % obj.pk
I was looking for a solution for adding a writable custom field to a model serializer. I found this one, which has not been covered in the answers to this question.
It seems like you do indeed need to write your own simple Serializer.
class PassThroughSerializer(serializers.Field):
def to_representation(self, instance):
# This function is for the direction: Instance -> Dict
# If you only need this, use a ReadOnlyField, or SerializerField
return None
def to_internal_value(self, data):
# This function is for the direction: Dict -> Instance
# Here you can manipulate the data if you need to.
return data
Now you can use this Serializer to add custom fields to a ModelSerializer
class MyModelSerializer(serializers.ModelSerializer)
my_custom_field = PassThroughSerializer()
def create(self, validated_data):
# now the key 'my_custom_field' is available in validated_data
...
return instance
This also works, if the Model MyModel
actually has a property called my_custom_field
but you want to ignore its validators.
After reading all the answers here my conclusion is that it is impossible to do this cleanly. You have to play dirty and do something hadkish like creating a write_only field and then override the validate
and to_representation
methods. This is what worked for me:
class FooSerializer(ModelSerializer):
foo = CharField(write_only=True)
class Meta:
model = Foo
fields = ["foo", ...]
def validate(self, data):
foo = data.pop("foo", None)
# Do what you want with your value
return super().validate(data)
def to_representation(self, instance):
data = super().to_representation(instance)
data["foo"] = whatever_you_want
return data