#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