2

I have a lot of similar fields that should be created in a similar way.

from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    is_friend = serializers.SerializerMethodField('get_is_friend')
    # ...

    def get_is_friend(self, obj):
        user = self.context['request'].user
        return user.id in obj.friend_list
    # ...

    class Meta:
        fields = (
            'is_friend',
            # ...
        )

Currently I have a decorator, it makes all I need, except creating SerializerMethodField.

def additional_fields_decorator(cls):
    relation_methods = (
        {
            'name': 'is_friend',
            'field_name': 'friends',
        },
        # ...
    )

    def relation_method_factory(field_name):
        def wrapper(self, obj):
            user = self.context['request'].user
            return user.id in getattr(obj, field_name)
        return wrapper

    for method in relation_methods:
        name = method['name']
        name_getter = 'get_{0}'.format(name)

        setattr(cls, name_getter, relation_method_factory(method['field_name']))
        cls.Meta.fields += (name,)
        # following line fails
        setattr(cls, name, serializers.SerializerMethodField(name_getter))

    return cls


@additional_fields_decorator
class UserSerializer(serializers.ModelSerializer):
    pass

Who knows why SerializerMethodField cannot be created outside of the Serializer class?

2 Answers 2

2

When DRF processes your UserSerializer class it looks for SerializerMethodField in the scope of the class. Hence when you declare the method outside of class it won't see it.

Sign up to request clarification or add additional context in comments.

1 Comment

So you want to say that I need to patch scope of wrapped class?
2

I recently encountered a similar problem and I think you can dynamically add SerializerMethodField by dynamically create a serializer class using the type built-in method. Something like this:

def generate_serializer(new_field):
    all_fields = ['field1', 'field2', new_field]

    class Meta:
        model = Model
        fields = all_fields

    def generate_new_field_method(key):
        def get_new_field(self, instance):
            # some logic based on field name
            pass
        return get_new_field

    dct = {
        'new_field': serializers.SerializerMethodField(),
        'get_new_field': generate_new_field_method(new_field),
        'Meta': Meta,
    }

    return type('DynamicSerializer', (serializers.ModelSerializer,), dct)

The above example dynamically add a single field, but you can easily modify it to add a list of fields.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.