Bradford Wade
Bradford Wade

Reputation: 505

Django Rest Framework Conditional Field on Serializer

I have a model that references a Generic Relation that I want to serialize in a detailed manner.

class AType(models.Model):
    foo = CharField()


class BType(models.Model):
    bar = PositiveIntegerField()


class ToSerialize(models.Model):
    scope_limit = models.Q(app_label="app", model="atype") | \
                  models.Q(app_label="app", model="btype")
    content_type = models.ForeignKey(ContentType, limit_choices_to=scope_limit)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')

I'd like the JSON for the list method of the ToSerialize viewset to look like:

[
    {
       "atype": { "id": 1, "foo": "a" }
    },
    {
       "atype": { "id": 2, "foo": "b" }
    },
    {
       "btype": { "id": 1, "bar": "1" }
    },
    {
       "btype": { "id": 2, "bar": "2" }
    }
]

Is there a way I can have the serializer for the ToSerialize object's viewset produce "conditional fields" based on the content_type/object_id that will achieve this effect?

Upvotes: 21

Views: 28621

Answers (3)

Pithikos
Pithikos

Reputation: 20300

None of the answers actually answer the question.

The easy thing to do is to add all the fields by default and then remove it at initialisation of the serializer depending on your condition.

In the example below, we don't give back the emails when listing users.

class UserSerializer():

    fields = ('username', 'email')

    class Meta:
        model = User

    def __init__(self, *args, **kwargs):

        # Don't return emails when listing users
        if kwargs['context']['view'].action == 'list':
            del self.fields['email']

        super().__init__(*args, **kwargs)

Upvotes: 21

jozo
jozo

Reputation: 4742

The recommended way is to create custom RelatedField. Check DRF docs about generic relationships for a nice example. In the OP case it would look like this:

class ABTypeRelatedField(serializers.RelatedField):

    def to_representation(self, value):
        """
        Serialize objects to a simple textual representation.
        """
        if isinstance(value, AType):
            return 'AType: ' + value.foo
        elif isinstance(value, BType):
            return 'BType: ' + value.bar
        raise Exception('Unexpected type of content_object')


class ToSerializeSerializer(serializers.Serializer):
    content_object = ABTypeRelatedField()

Upvotes: 4

Sam R.
Sam R.

Reputation: 16450

Use SerializeMethodField:

class YourSerializer(serializers.ModelSerializer):
    your_conditional_field = serializers.SerializerMethodField()

    class Meta:
        model = ToSerialize

    def get_your_conditional_field(self, obj):
        # do your conditional logic here
        # and return appropriate result
        return obj.content_type > obj.object_id

Upvotes: 13

Related Questions