Как отобразить значение модели ForeignKey в Django Rest Framework

models.py

class Item(models.Model):
    name = models.CharField(max_length=100)
    # other_no_need_translate = models.CharField(max_length=100)

class Itemi18n(models.Model):
    origin = models.ForeignKey(Item, related_name='i18n')
    language = models.CharField(max_length=200, default="")
    name = models.CharField(max_length=100)

view.py

class ItemViewSet(viewsets.GenericViewSet):
    
    def get_queryset(self):
        return Item.objects.annotate(name=F('i18n__name')) # something like this

serializers.py

class I18nField(Field):

    def __init__(self, field_attr, **kwargs):
        self.field_attr = field_attr
        super().__init__(source='*', read_only=True, **kwargs)

    def to_representation(self, value) -> str:
        request = self.context['request']
        if request.query_params.get('lang') == 'es':
            value = ...
        else:
            value = ...
        return value

class ItemSerializer(serializers.ModelSerializer):
    name = I18nField(...)

Я хочу написать сериализатор, который при необходимости i18n возвращает имя из Itemi18n, в противном случае возвращает имя из Item. Я придумал 2 решения, одно - использовать аннотацию для переопределения поля, другое - использовать Custom Field. Я не уверен в лучших практиках и проблемах с производительностью.

С точки зрения производительности, аннотация будет хуже, поскольку она будет выполнять агрегирование по всем объектам. Лучшим подходом будет, конечно, максимально сузить количество запросов.

Поскольку эта функция является мутацией исходного значения, я считаю, что лучше всего реализовать ее внутри метода serializer to_representation.

from django.utils.translation import get_language


class ItemModelSerializer(ModelSerializer):
    class Meta:
        model = Item
        exclude = ["id"]

    def to_representation(self, instance):
        representation = super().to_representation(instance)
        local_lang = get_language()
        if local_lang != "en":
            try:
                translation = instance.i18n.get(language=local_lang)
                representation["name"] = translation.name

            except Itemi18n.DoesNotExist:
                pass

        return representation

P.S. Также можно увеличить производительность за счет перехвата связанных объектов.

Вернуться на верх