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(...)
I want to write a serializer, when i18n is needed, return name from Itemi18n, otherwise return name from Item. I thought of 2 solutions,one is use annotate to override field, another is use Custom Field. I am not sure about best practices and performance issues.
Performance wise, the annotation would be worse, since that would perform aggregation to all objects. The best approach would be to narrow down the number of queries as much as possible of course.
Since that feature is a mutation of the original value, I belive the best place to implement this is inside the serializer
to_representation
method.
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. It is also possible to increase performance by prefetching related objects.