Reputation: 23526
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?
Upvotes: 123
Views: 133371
Reputation: 1403
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
Upvotes: 119
Reputation: 3611
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
Upvotes: 17
Reputation: 14803
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.
Upvotes: 11
Reputation: 5496
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')
Upvotes: 13
Reputation: 14811
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',)
Upvotes: 7
Reputation: 3461
...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',
)
Upvotes: 20
Reputation: 157
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')
source: https://stackoverflow.com/a/18396622/3220916
Upvotes: 14
Reputation: 33921
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')
Upvotes: 66