Rahul Sarma
Rahul Sarma

Reputation: 427

Dynamically create serializer based on model field value

I have a model like so:

class A:
    name = models.CharField()
    group = models.ForeignKey('SomeModel', null=True, blank=True)

When I serialize this, I would like the serielizer to have different formats based on whether the 'group' field is blank or not. Of course this can be achieved by having different serializers for different formats and calling them as required in the View layer:

class TypeASerializer(serializers.ModelSerializer)

    class Meta:
        model = A
        fields = ('id', 'name')

class TypeBSerializer(serializers.ModelSerializer)

    class Meta:
        model = A
        fields = ('id', 'name', 'group')

But I wanted to handle it in the serializer layer itself and have a single serializer for this. Is that possible?

Upvotes: 1

Views: 2602

Answers (3)

Hyeonjong Gim
Hyeonjong Gim

Reputation: 21

Serializer.instance may be None in some cases.

And get_fields() is called only once because Serializer.fields is cached from django-rest-framework 3.10: https://github.com/encode/django-rest-framework/commit/7232586c7caf66f20f56b36f1c6a9c9648eb94a4

In other words, when a serializer is used as a list by many=True (in ListModelMixin, or as a field of another serializer), the fields of all items in the list are determined by the first instance.

In that case, the solution is to override to_representation():

class TypeASerializer(serializers.ModelSerializer)
    class Meta:
        model = A
        fields = ('id', 'name', 'group')

    def to_representation(self, instance):
        ret = super().to_representation(instance)
        if not instance.group:
            del ret['group']
        return ret

This solution is a little inefficient because all fields and values are obtained from super().to_presentation() but some of them are removed again. You can consider fully implementing to_representation() without calling super's.

Upvotes: 2

aman kumar
aman kumar

Reputation: 3156

you can override the get_fields methods of serializer

class YourSerializer(serializers.ModelSerializer):
   id = serializers.SerializerMethodField()
   name = serializers.SerializerMethodField()
   group = serializers.SerializerMethodField()

   class Meta:
      model = A
      fields = ('id', 'name', 'group')

   def get_fields(self):
      fields = super().get_fields()
      # delete all the unnecessary fields according to your logic.
      if self.instance.group:  # if this is detials view other wise pass object in context
         del fields['group']
      return fields 

Upvotes: 1

vishes_shell
vishes_shell

Reputation: 23484

You can declare every field of your serializer as SerializerMethodField as follows:

class YourSerializer(serializers.ModelSerializer):
    id = serializers.SerializerMethodField()
    name = serializers.SerializerMethodField()
    group = serializers.SerializerMethodField()

    class Meta:
        model = A
        fields = ('id', 'name', 'group')

    def id(self, obj):
        if yourcondition(obj.group):
            return obj.id
        return another_value
    ...

Upvotes: 0

Related Questions