Home > Enterprise >  Django REST Framework: Does ModelSerializer have an option to change the fields dynamically by GET o
Django REST Framework: Does ModelSerializer have an option to change the fields dynamically by GET o

Time:10-24

Does ModelSerializer have an option to change the fields dynamically by GET or (POST, PUT, DELETE)?

While GET requires complex fields such as nested serializers, these are not required for (POST, PUT, DELETE).

I think the solution is to use separate serializers for GET and (POST, PUT, DELETE). But in that case, I'd have to create quite a few useless serializers. Is there any good solution?

class PlaylistSerializer(serializers.ModelSerializer):
    user = UserDetailSerializer(read_only=True)
    tracks = serializers.SerializerMethodField()
    is_owner = serializers.SerializerMethodField()
    is_added = serializers.SerializerMethodField()
    is_favorited = serializers.BooleanField()

    class Meta:
        model = Playlist
        fields = (
            "pk",
            "user",
            "title",
            "views",
            "is_public",
            "is_wl",
            "created_at",
            "updated_at",
            "tracks",
            "is_owner",
            "is_added",
            "is_favorited",
        )

    def get_is_owner(self, obj):
        return obj.user == self.context["request"].user

    def get_tracks(self, obj):
        queryset = obj.track_set
        if queryset.exists():
            tracks = TrackSerializer(queryset, context=self.context, many=True).data
            return tracks
        else:
            return []

    def get_is_added(self, obj):
        try:
            return obj.is_added
        except AttributeError:
            return False


class PlaylistUpdateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Playlist
        fields = ("title", "is_public")

CodePudding user response:

first you need to create a class and inherit your serializer from this class as below:

from rest_framework import serializers


class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    """To be used alongside DRF's serializers.ModelSerializer"""
    @classmethod
    def default_fieldset(cls):
       return cls.Meta.fields

    def __init__(self, *args, **kwargs):
        self.requested_fields = self._extract_fieldset(**kwargs)
        # Fields should be popped otherwise next line complains about 
unexpected kwarg
        kwargs.pop('fields', None)
        super().__init__(*args, **kwargs)
        self._limit_fields(self.requested_fields)

    def _extract_fieldset(self, **kwargs):
        requested_fields = kwargs.pop('fields', None)
        if requested_fields is not None:
            return requested_fields

        context = kwargs.pop('context', None)
        if context is None:
            return None

        return context.get('fields')

    def _limit_fields(self, allowed_fields=None):
        if allowed_fields is None:
            to_exclude = set(self.fields.keys()) - set(self.default_fieldset())
        else:
            to_exclude = set(self.fields.keys()) - set(allowed_fields)
        for field_name in to_exclude or []:
            self.fields.pop(field_name)

    @classmethod
    def all_fields_minus(cls, *removed_fields):
        return set(cls.Meta.fields) - set(removed_fields)

then your serializer would be something like this:

class PlaylistSerializer(DynamicFieldsModelSerializer):
    class Meta:
        model = Playlist
        fields = ("pk", "user", "title", "views", "is_public",
        "is_wl", "created_at", "updated_at", "tracks", "is_owner",
        "is_added", "is_favorited",)

    @classmethod
    def update_serializer(cls):
        return ("title", "is_public")
    
    @classmethod
    def view_serializer(cls):
        return ("title", "is_public", "is_owner", "is_added")

then you will call your serializer as below:

PlaylistSerializer(instance, fields=PlaylistSerializer.update_serializer()).data
  • Related