82

How do add a non-model field on a ModelSerializer in DRF 3? i.e. add a field that does not exist on my actual model?

class TestSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='vote_detail')
    non_field = serializers.CharField()  # no corresponding model property.


    class Meta:
        model = vote_model
        fields = ("url", "non_field")

    def create(self, validated_data):
      print(direction=validated_data['non_field'])

But DRF 3 gives me the error:

Got AttributeError when attempting to get a value for field `non_field` on serializer `TestSerializer`.
The serializer field might be named incorrectly and not match any attribute or key on the `Test` instance.
Original exception text was: 'Test' object has no attribute 'non_field'.

I have searched stack DRF - ModelSerializer with a non-model write_only field and found a few solutions but these refer to DRF 2 where I'm using DRF 3. Is there a solution for this on this version?

Community
  • 1
  • 1
Prometheus
  • 32,405
  • 54
  • 166
  • 302
  • 1
    https://github.com/tomchristie/django-rest-framework/issues/840 – chandu Jun 02 '15 at 14:07
  • 1
    http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield – chandu Jun 02 '15 at 14:11
  • @chandu so is the issue on going as its not fixed in 3.0+ or is there now a solution to this? – Prometheus Jun 02 '15 at 14:27
  • you may use this method or below method http://www.django-rest-framework.org/api-guide/fields/#serializermethodfield – chandu Jun 02 '15 at 14:29
  • http://stackoverflow.com/questions/18396547/django-rest-framework-adding-additional-field-to-modelserializer – chandu Jun 02 '15 at 14:31
  • @chandu SerializerMethodField will not work as I need the non_field to be writable. using a SerializerMethodField does not allow me access to it using ``validated_data['non_field']`` from what I have tested anyhow. – Prometheus Jun 02 '15 at 14:41
  • 1
    I solved it using a`` @property`` on the model from the answer you posted above. Can you post and answer so I may accept? – Prometheus Jun 02 '15 at 14:45
  • @OrbiterFleetposted the answer below. – chandu Jun 03 '15 at 05:25

8 Answers8

117
class MySerializer(serializers.ModelSerializer):
    write_only_char_field = serializers.CharField(write_only=True)
    write_only_list_char_field = serializers.ListField(child=serializers.CharField(max_length=100, default=''), write_only=True)
    empty_method_field = serializers.SerializerMethodField()
    read_only_custom_model_field = serializers.CharField(source='custom_property', read_only=True)

    def create(self, validated_data):
        validated_data.pop('write_only_char_field', None)
        validated_data.pop('write_only_list_char_field', None)
        return super().create(validated_data)

The serializers.CharField(write_only=True) and serializers.ListField(...) is a good solution to provide extra data to your .create() and .update() methods, as either a single string or a list of strings (you can mix ListField with other serializer field types).
With this method, you can also define def validate_write_only_char_field to implement some quick and simple validation.

serializers.SerializerMethodField() allows you to add some custom read-only field to your serializer output from a method defined on the serializer.

The read_only_custom_model_field would use a method on your model to read some data, not strictly a model field, but a custom method. I.e.

class MyModel(models.Model):
    my_field = models.CharField(max_length=100)

    @property
    def custom_property(self):
        return "Perform calculations, combine with related models, etc. etc."
A. J. Parr
  • 7,731
  • 2
  • 31
  • 46
4
class Foo(models.Model):
    . . .
    @property
    def my_field(self):
        return stuff
    . . .

Source:

Django REST Framework: adding additional field to ModelSerializer

Community
  • 1
  • 1
chandu
  • 1,053
  • 9
  • 18
2

Just an example might help you.

  class ExtensibleModelSerializerOptions(serializers.SerializerOptions):
    """
    Meta class options for ModelSerializer
    """
    def __init__(self, meta):
        super(ExtensibleModelSerializerOptions, self).__init__(meta)
        self.model = getattr(meta, 'model', None)
        self.read_only_fields = getattr(meta, 'read_only_fields', ())
        self.non_native_fields = getattr(meta, 'non_native_fields', ())


class ExtensibleModelSerializer(serializers.ModelSerializer):

    _options_class = ExtensibleModelSerializerOptions

    def restore_object(self, attrs, instance=None):
        """
        Deserialize a dictionary of attributes into an object instance.
        You should override this method to control how deserialized objects
        are instantiated.
        """
        for field in self.opts.non_native_fields:
            attrs.pop(field)

        return super(ExtensibleModelSerializer, self).restore_object(attrs, instance)

Source: https://github.com/tomchristie/django-rest-framework/issues/951

chandu
  • 1,053
  • 9
  • 18
2

Just give Serializer-field in serializer like this,

name = serializers.BooleanField(allow_null=True, required=False, default=False)

after then use create function in serializers like this

def create(self, validated_data):
    validated_data.pop('name', None)
    return super().create(validated_data)

Now you can enter the data from frontend-side and it will not save in the model.
And to access the data of this field you will get it in create function of view.

Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
1

As mentioned there are two ways. (1) adding a model property. (2) adding a model field. I feel that adding a @property to model was explained well in this post. If you want to keep your models "lean and mean" use a Method field. Chandus answer omits some crucial points, though:

class DeliveryItemSerializer(serializers.ModelSerializer):

    product_name = serializers.SerializerMethodField(read_only=True)

    def get_product_name(self, obj):
        return obj.product.name

    class Meta:
        model = DeliveryItem
        fields = (
            (...your field names),
            'product_name',)
  1. Set read_only
  2. The field and the corresponding method do not have the same name. The method name defaults to get_field_name. If you use another name use the method_name=method name argument on SerializerMethodField()
Xen_mar
  • 8,330
  • 11
  • 51
  • 74
0
class TestSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='vote_detail')
    non_field = serializers.SerializerMethodField()  # no corresponding model property.

    class Meta:
        model = vote_model
        fields = ("url", "non_field")

    def create(self, validated_data):
        print(direction=validated_data['non_field'])

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

or go through this link

Community
  • 1
  • 1
chandu
  • 1,053
  • 9
  • 18
  • non_field = serializers.serializers.SerializerMethodField() # no corresponding model property - remove the extra serializers. ? – Liz Feb 23 '16 at 23:11
  • 2
    I'm trying to do exactly the same thing but I have an error telling me that my model doesn't have this field. Does anyone found a solution? – Ben Nov 22 '16 at 16:31
  • 9
    This answer is wrong. The first thing it says in the link above, https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield, is that SerializerMethodField is a read-only field. – kylebebak Jun 04 '19 at 08:01
0

In all previous answer what i saw every one recommended to remove those added fields which is not the part of Django model..

So this is not follow single responsibility principal

def create(self, validated_data):
    pass

According to drf drf doc this function only responsible for creation.

but if you want just add some extra fields in response we need to override

def to_representation(self,instance):
    pass
Sonu kumar
  • 43
  • 5
-1
def to_representation(self, instance):
    return {}

add this to your create method

Bibek
  • 21
  • 5
  • This answer requires more explanation otherwise it will get deleted – Cornelius Roemer Feb 13 '23 at 23:32
  • 1
    @CorneliusRoemer: FYI: While answers _should_ include explanations, and that will certainly make them more _useful_, they should only be _deleted_ if they aren't actually answering the question. Code-only answers may be downvoted, but should otherwise be marked as "Looks OK" in the Low Quality Answers queue. ([Source](https://meta.stackoverflow.com/questions/287563/youre-doing-it-wrong-a-plea-for-sanity-in-the-low-quality-posts-queue).) – Jeremy Caney Feb 14 '23 at 00:21