DRF Как выбрать определенные поля для отображения во вложенных отношениях сериализатора? (без дополнительных сериализаторов)
У меня есть сериализатор
class CategoryListSerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ["id", "name", "name_en", "about", "parent",]
Он используется в двух местах:
- API "Все категории": Используется для просмотра подробной информации о категориях.
- All Posts API: Используется для того, чтобы узнать только название категории.
В своем сериализаторе сообщений я использовал:
class PostListSerializer(serializers.ModelSerializer):
categories = CategoryListSerializer(many=True, )
class Meta:
model = Post
fields = ["id", "title", "description", "publish_date", "thumbnail", "owner", "categories", ]
А в моем посте ViewSet:
class PostViewSet(ReadOnlyModelViewSet):
queryset = Post.objects.all().filter(is_published=True)
serializer_class = PostListSerializer
Возвращаются все посты со всеми категориями, указанными в CategoryListSerializer, как и должно быть.
Вопрос:
Я хочу, чтобы PostListSerializer возвращало только поле "имя" из связанных Категорий, без необходимости определять другое CategorySimpleSerializer, которое выбирает только поле "имя". (Мне все еще нужны поля CategoryListSerializer в другом API)
Возможно ли это сделать?
Примечание: Это только пример, у меня будет больше примеров для этого и я хочу знать заранее, придется ли мне создавать много пользовательских "вложенных" Serialzers, чтобы избежать раскрытия некоторых ненужных данных для некоторых API. Это похоже на множество лишней работы по обновлению, если модель или потребности API изменятся позже.
Об этом упоминает @mtzd в комментариях:
Создание общего класса динамического сериализатора (как в DRF Docs здесь) сработало!
Мой сериализатор категорий выглядит следующим образом:
class DynamicFieldsCategorySerializer(serializers.ModelSerializer):
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().__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 CategoryListSerializer(DynamicFieldsCategorySerializer):
class Meta:
model = Category
fields = [ "name", "name_en", "about",]
и в переменной PostListSerializer Categories я добавил атрибут fields only:
categories = CategoryListSerializer(many=True, fields=['name',])
Теперь я могу управлять тем, какие поля показывать каждому View API, который я использую, из единого сериализатора модели, который я могу изменить/обновить один раз.
Вам следует использовать сериализаторы, как описано ниже, для ваших случаев использования.
class PostListSerializer(serializers.ModelSerializer):
categories = serializers.SerializerMethodField('get_categories')
class Meta:
model = Post
fields = ["id", "title", "description", "publish_date", "thumbnail", "owner", "categories", ]
def get_categories(self, obj):
return obj.categories.all().values("name")
Также вам необходимо оптимизировать ваши
Post.objects.all().filter(is_published=True) до Post.objects.filter(is_published=True).select_related("categories")
class Meta:
read_only_fields = (
"id",
"slug",
)