Структура Django REST: Есть ли у ModelSerializer возможность динамически изменять поля с помощью GET или (ОПУБЛИКОВАТЬ, ПОМЕСТИТЬ, УДАЛИТЬ)?

#django #django-rest-framework

Вопрос:

Есть ли у ModelSerializer возможность динамически изменять поля с помощью GET или (ОПУБЛИКОВАТЬ, ПОМЕСТИТЬ, УДАЛИТЬ)?

В то время как GET требует сложных полей, таких как вложенные сериализаторы, они не требуются для (POST, PUT, DELETE).

Я думаю, что решение состоит в использовании отдельных сериализаторов для GET и (POST, PUT, DELETE). Но в этом случае мне пришлось бы создать довольно много бесполезных сериализаторов. Есть ли какое-нибудь хорошее решение?

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

Комментарии:

1. Что вы подразумеваете под «довольно большим количеством бесполезных сериализаторов»? Что плохого в том, чтобы иметь несколько сериализаторов, а затем установить сериализатор в представлении на основе метода HTTP запроса?

2. Я просто подумал, что создать сериализатор для каждого метода HTTP непросто, но нормально ли это?

3. Вы можете изменить один сериализатор для работы с любым методом запроса. Предполагается, что представление или набор представлений обрабатывают операции crud на основе методов запроса. А также подробнее остановитесь на своем вопросе, приведя пример.

4. Не могли бы вы, пожалуйста, показать мне код для справки?

5. Мне жаль. Я думал, что отредактировал его, но не сделал этого. Я хочу объединить эти сериализаторы в один, и когда я вставляю, я не хочу возвращать ничего, кроме двух полей, как в PlaylistUploadSerializer.

Ответ №1:

сначала вам нужно создать класс и унаследовать сериализатор от этого класса, как показано ниже:

 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)
 

тогда ваш сериализатор будет выглядеть примерно так:

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

затем вы вызовете свой сериализатор, как показано ниже:

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