Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I want to serialize a model, but want to include an additional field that requires doing some database lookups on the model instance to be serialized:

class FooSerializer(serializers.ModelSerializer):
  my_field = ... # result of some database queries on the input Foo object
  class Meta:
        model = Foo
        fields = ('id', 'name', 'myfield')

What is the right way to do this? I see that you can pass in extra "context" to the serializer, is the right answer to pass in the additional field in a context dictionary?

With that approach, the logic of getting the field I need would not be self-contained with the serializer definition, which is ideal since every serialized instance will need my_field. Elsewhere in the DRF serializers documentation it says "extra fields can correspond to any property or callable on the model". Are "extra fields" what I'm talking about?

Should I define a function in Foo's model definition that returns my_field value, and in the serializer I hook up my_field to that callable? What does that look like?

Happy to clarify the question if necessary.

I think SerializerMethodField is what you're looking for:

class FooSerializer(serializers.ModelSerializer):
  my_field = serializers.SerializerMethodField('is_named_bar')
  def is_named_bar(self, foo):
      return foo.name == "bar" 
  class Meta:
    model = Foo
    fields = ('id', 'name', 'my_field')

http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield

is it possible to add validation to such fields? my question is: how to accept custom POST values which can be validated and processes in the post_save() handler? – Alp May 30, 2014 at 10:34 Note that SerializerMethodField is read-only, so this won't work for incoming POST/PUT/PATCH. – Scott A Aug 4, 2015 at 19:12 In DRF 3, it is changed to field_name = serializers.SerializerMethodField() and def get_field_name(self, obj): – Chemical Programmer Dec 9, 2015 at 13:25 whats the foo when def a SerializerMethodField ? when use CreateAPIView, is the foo have stored then can use the is_named_bar() method? – user7693832 Nov 21, 2017 at 8:01 "foo" here should be "instance" as it's the instance currently being 'seen' by the Serializer. – Tom O'Connor Oct 1, 2020 at 19:29

You can change your model method to property and use it in serializer with this approach.

class Foo(models.Model):
    . . .
    @property
    def my_field(self):
        return stuff
    . . .
class FooSerializer(ModelSerializer):
    my_field = serializers.ReadOnlyField(source='my_field')
    class Meta:
        model = Foo
        fields = ('my_field',)

Edit: With recent versions of rest framework (I tried 3.3.3), you don't need to change to property. Model method will just work fine.

Thanks @Wasil! I am not familiar with the use of properties in Django models, and cannot find good explanation of what it means. Can you explain? What is the point of the @property decorator here? – Neil Aug 23, 2013 at 7:09 it means you can call this method like a property: i.e. variable = model_instance.my_field gives the same result as variable = model_instance.my_field() without the decorator. also: stackoverflow.com/a/6618176/2198571 – Wasil W. Siargiejczyk Aug 23, 2013 at 7:36 This is not working, at least in Django 1.5.1 / djangorestframework==2.3.10. The ModelSerializer is not getting the propery even if explictly referred in "fields" Meta attribute. – ygneo Mar 23, 2014 at 19:12 you need to add the field to the serializer because it's no real modelfield: my_field = serializers.Field(source='my_field') – Marius Apr 4, 2014 at 9:00

if you want read and write on your extra field, you can use a new custom serializer, that extends serializers.Serializer, and use it like this

class ExtraFieldSerializer(serializers.Serializer):
    def to_representation(self, instance): 
        # this would have the same as body as in a SerializerMethodField
        return 'my logic here'
    def to_internal_value(self, data):
        # This must return a dictionary that will be used to
        # update the caller's validation data, i.e. if the result
        # produced should just be set back into the field that this
        # serializer is set to, return the following:
        return {
          self.field_name: 'Any python object made with data: %s' % data
class MyModelSerializer(serializers.ModelSerializer):
    my_extra_field = ExtraFieldSerializer(source='*')
    class Meta:
        model = MyModel
        fields = ['id', 'my_extra_field']

i use this in related nested fields with some custom logic

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. No need for @property and source='field' raise an error.

class Foo(models.Model):
    . . .
    def foo(self):
        return 'stuff'
    . . .
class FooSerializer(ModelSerializer):
    foo = serializers.ReadOnlyField()
    class Meta:
        model = Foo
        fields = ('foo',)
                what if i want to have request object inside def foo(self) which could modify foo's value? (example a request.user based lookup)
– saran3h
                Jan 22, 2019 at 18:03

My response to a similar question (here) might be useful.

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

If you want to add field dynamically for each object u can use to_represention.

class FooSerializer(serializers.ModelSerializer):
  class Meta:
        model = Foo
        fields = ('id', 'name',)
  def to_representation(self, instance):
      representation = super().to_representation(instance)
      if instance.name!='': #condition
         representation['email']=instance.name+"@xyz.com"#adding key and value
         representation['currency']=instance.task.profile.currency #adding key and value some other relation field
         return representation
      return representation

In this way you can add key and value for each obj dynamically hope u like it

This worked for me. If we want to just add an additional field in ModelSerializer, we can do it like below, and also the field can be assigned some val after some calculations of lookup. Or in some cases, if we want to send the parameters in API response.

In model.py

class Foo(models.Model):
    """Model Foo"""
    name = models.CharField(max_length=30, help_text="Customer Name")

In serializer.py

class FooSerializer(serializers.ModelSerializer):
    retrieved_time = serializers.SerializerMethodField()
    @classmethod
    def get_retrieved_time(self, object):
        """getter method to add field retrieved_time"""
        return None
  class Meta:
        model = Foo
        fields = ('id', 'name', 'retrieved_time ')

Hope this could help someone.

If you want to use the same property name:

class DemoSerializer(serializers.ModelSerializer):
    property_name = serializers.ReadOnlyField()
    class Meta:
        model = Product
        fields = '__all__' # or you can choose your own fields

If you want to use different property name, just change this:

new_property_name = serializers.ReadOnlyField(source='property_name')

Even though, this is not what author has wanted, it still can be considered useful for people here:

If you are using .save() ModelSerializer's method, you can pass **kwargs into it. By this, you can save multiple dynamic values.

i.e. .save(**{'foo':'bar', 'lorem':'ipsum'})

this really saved me. why is this so hard to figure out? so much of working with DRF seems so opaque. all i wanted was to upload an image and set an "uploaded_by" field with the request.user and that took me like days of reading documentation before finding this. – Nathaniel Hoyt Feb 28 at 20:31 I've got your pain, the documentation for DRF is really opaque (sometimes) and almost every Django-related question on SO can go to a high school already – Limtis Feb 28 at 22:05

As Chemical Programer said in this comment, in latest DRF you can just do it like this:

class FooSerializer(serializers.ModelSerializer):
    extra_field = serializers.SerializerMethodField()
    def get_extra_field(self, foo_instance):
        return foo_instance.a + foo_instance.b
    class Meta:
        model = Foo
        fields = ('extra_field', ...)

DRF docs source

Add the following in serializer class:

def to_representation(self, instance):
    representation = super().to_representation(instance)
    representation['package_id'] = "custom value"
    return representation
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.

DjangoRestFramework - How to create a Serializer ReadOnlyField who's value depends on the user? See more linked questions