简体   繁体   中英

Add a non-model field on a ModelSerializer in DRF 3

How do add a non-model field on a ModelSerializer in DRF 3? ie 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?

class MySerializer(serializers.ModelSerializer):
    write_only_char_field = serializer.CharField(write_only=True)
    write_only_list_char_field = serializer.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. Ie

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."
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

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

class Foo(models.Model):
    . . .
    @property
    def my_field(self):
        return stuff
    . . .

Source:

Django REST Framework: adding additional field to ModelSerializer

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()

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM