django-parler-rest
django-parler-rest copied to clipboard
Serializer : Load only languages requested in headers
I really like this implementation. It seems much more qualitative than the one we implemented on our project.
However, It is possible to load only languages requested in accept_language
headers ?
And maybe the default language if the string is not translated into the desired language?
Example
{
"id": 528,
"country_code": "NL",
"translations": {
"default_language_code": {
"name": "English",
"welcome_message": "Welcome !"
},
"header_language_code": {
"name": "French",
"welcome_message": "Bienvenue"
},
}
}
Because it can quickly produce heavy schemas to return strings in all enabled languages. Thank you in advance
It would be great feature and strong optimization. I have plans to fetch from DB whole and big articles, only for desired language. In django tastypie it works like a charm.
Maybe this mixin will help you. Depending on given accept-language
header, it returns a flat representation of the model without the translations sub-fields. If the language was not found, the fallback value is used:
`from django.conf import settings
class TranslatedSerializerMixin(object): """ Mixin for selecting only requested translation with django-parler-rest """
def to_representation(self, instance):
inst_rep = super().to_representation(instance)
request = self.context.get('request')
lang_code = request.META.get('HTTP_ACCEPT_LANGUAGE', None)
# only use the first two chars for language code
if lang_code and '-' in lang_code:
lang_code = lang_code.split('-')[0]
result = {}
for field_name, field in self.get_fields().items():
# add normal field to resulting representation
if field_name is not 'translations':
field_value = inst_rep.pop(field_name)
result.update({field_name: field_value})
if field_name is 'translations':
translations = inst_rep.pop(field_name)
if lang_code not in translations:
# use fallback setting in PARLER_LANGUAGES
parler_default_settings = settings.PARLER_LANGUAGES['default']
if 'fallback' in parler_default_settings:
lang_code = parler_default_settings.get('fallback')
if 'fallbacks' in parler_default_settings:
lang_code = parler_default_settings.get('fallbacks')[0]
for lang, translation_fields in translations.items():
if lang == lang_code:
trans_rep = translation_fields.copy() # make copy to use pop() from
for trans_field_name, trans_field in translation_fields.items():
field_value = trans_rep.pop(trans_field_name)
result.update({trans_field_name: field_value})
return result
`
Thanks @chrda81, your solution is very impressive.
I made some changes on your code to take a different type of output.
class TranslatedSerializerMixin(object):
"""
Get values without "translations" key.
For selecting language; add "Accept-Language" into request header.
There is no dependence to django-parler-rest library.
Usage:
class MyModel(models.Model):
image = ...
translations = TranslatedFields(
name = models.CharField(...),
description = models.TextField(...)
)
class MySerializer(TranslatedSerializerMixin, serializers.ModelSerializer):
class Meta:
model = MyModel
fields = (
'id',
'image',
'name',
'description',
)
result:
[
{
id: 1,
image: '/images/001.png,
name: '<value for the selected language>'
description: '<value for the selected language>'
},
...
]
"""
def to_representation(self, instance):
inst_rep = super().to_representation(instance)
request = self.context.get('request')
lang_code = request.META.get('HTTP_ACCEPT_LANGUAGE', None)
# Only use the first two chars for language code
if lang_code and '-' in lang_code:
lang_code = lang_code.split('-')[0]
result = {}
translation_fields = self.get_translations_fields()
translation_model = self.Meta.model._parler_meta.root_model
translate = None
# No need to get translate values while language is default
if lang_code != DEFAULT_LANG:
try:
translate = translation_model.objects.get(
master=instance,
language_code=lang_code
)
except:
...
for field_name, field in self.get_fields().items():
field_value = inst_rep.pop(field_name)
if translate and field_name in translation_fields:
translate_value = getattr(translate, field_name)
if translate_value is not None:
field_value = translate_value
result[field_name] = field_value
return result
def get_translations_fields(self):
""" Return list of translate fields name"""
return self.Meta.model._parler_meta.get_all_fields()```