@mi1ord

Проблема в DRF при работе с dynamically modifying fields. Что можно сделать?

Чтобы не писать кучу сериализаторов решил попробовать dynamically modifying fields. Делал все согласно документации, то есть определил сериализатор:
class DynamicFieldsModelSerializer(serializers.ModelSerializer):
    """
    A ModelSerializer that takes an additional `fields` argument that
    controls which fields should be displayed.
    """

    def __init__(self, *args, **kwargs):
        # Don't pass the 'fields' arg up to the superclass
        fields = kwargs.pop('fields', None)

        # Instantiate the superclass normally
        super(DynamicFieldsModelSerializer, self).__init__(*args, **kwargs)

        if fields is not None:
            # Drop any fields that are not specified in the `fields` argument.
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)

И унаследовался от него:
class TrainingSerializer(DynamicFieldsModelSerializer):
    trainer = TrainerSerializer()
    url = serializers.CharField(source='get_absolute_url', read_only=True)

    class Meta:
        model = Training
        fields = ['title', 'description', 'slug', 'trainer', 'url', 'dates']


Собственно проблема в следующем: как вызвать TrainingSerializer во вьшке с нужными мне полями?
Попытка сделать вот так конечно же приводит к исключению TypeError: 'TrainingSerializer' object is not callable.

class TrainingDetailAPIView(RetrieveAPIView):
    queryset = Training.objects.all()
    serializer_class = TrainingSerializer(fields=('title', 'description'))
    lookup_field = 'slug'


Пробовал через shell - все работает преотлично:
TrainingSerializer(fields=('title', 'description')):
    title = CharField(max_length=250)
    description = CharField(style={'base_template': 'textarea.html'})
  • Вопрос задан
  • 68 просмотров
Решения вопроса 1
@javedimka
Хочу сока
Оригинальный ответ
from rest_framework.response import Response


class TrainingDetailAPIView(RetrieveAPIView):
    queryset = Training.objects.all()
    serializer_class = TrainingSerializer
    lookup_field = 'slug'
    
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance, fields=('title', 'description'))
        return Response(serializer.data)

При работе с drf рекомендую на отдельной вкладке держать открытым гитхаб с сорцами.

UPD:
Оригинальное решение хоть и работает, но слегка кривоватое(первое что пришло в голову), правильнее будет переписать метод get_serializer():
class TrainingDetailAPIView(RetrieveAPIView):
    queryset = Training.objects.all()
    serializer_class = TrainingSerializer
    lookup_field = 'slug'
    
    def get_serializer(self, *args, **kwargs):
        kwargs['fields'] = ('title', 'description')
        return super().get_serializer(*args, **kwargs)

Меньше кода и не происходит его дупликации как в первом варианте при переписывании метода из retrieve миксина.
Если такая вью не единственная, то следует пойти дальше и написать свой миксин:
class ShowFieldsMixin:

    def get_serializer(self, *args, **kwargs):
        if getattr(self, 'fields_to_show', None) is not None:
            kwargs['fields'] = self.fields_to_show
        return super().get_serializer(*args, **kwargs)


class TrainingDetailAPIView(ShowFieldsMixin, RetrieveAPIView):
    queryset = Training.objects.all()
    serializer_class = TrainingSerializer
    lookup_field = 'slug'
    fields_to_show = ('title', 'description')

class TrainingListAPIView(ShowFieldsMixin, ListAPIView):
    # bla-bla-bla
    serializer_class = TrainingSerializer
    fields_to_show = ('name', 'description', 'url', 'slug')
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через TM ID
Похожие вопросы