django-modeltranslation
django-modeltranslation copied to clipboard
I wrote a Mixin for a DRF-API-View to dynamically support django-modeltranslation
I wrote a Mixin for the django-restframework API View to automatically deliver translations. Here is an example for German and English activated:
class AchievementSerializer(TranslatableSerializerMixin, serializers.ModelSerializer):
class Meta:
model = Achievement
fields = ('slug', 'title',)
When you now make a GET request to it, you will get this as a return:
{
"count": 6,
"current_page": 1,
"results": [{
"slug": "campaign",
"title": "Kampagne",
"title_de": "Kampagne",
"title_en": "Campaign"
}, {
"slug": "profile",
"title": "Profil",
"title_de": "Profil",
"title_en": "Profile"
}, {
"slug": "promote-project",
"title": "Projekt bekannt machen",
"title_de": "Projekt bekannt machen",
"title_en": "Promote Project"
}, {
"slug": "company",
"title": "Unternehmen",
"title_de": "Unternehmen",
"title_en": "Company"
}, {
"slug": "organization",
"title": "Organisation",
"title_de": "Organisation",
"title_en": "Organization"
}, {
"slug": "project",
"title": "Projekt",
"title_de": "Projekt",
"title_en": "Project"
}],
"links": {
"previous": null,
"next": null
}
}
You can also POST to it like normal.
I'm really interested in this topic. Could you share the TranslatableSerializerMixin code?
Thanks in advance!
@iekadou How we can see the code of your mixin? Why didn't you create a pull request?
bump, can you share the mixin @iekadou ?
Here's a simple implementation of the above (mixin renamed). By default, it will provide all translations of the serializer fields. However, if the language get parameter is provided to the request as a comma delimited string, then only those specified translations will be returned.
from django.conf import settings
from modeltranslation.manager import get_translatable_fields_for_model
from rest_framework import serializers
class TranslatedModelSerializerMixin(serializers.ModelSerializer):
def get_field_names(self, declared_fields, info):
fields = super().get_field_names(declared_fields, info)
trans_fields = get_translatable_fields_for_model(self.Meta.model)
all_fields = []
requested_langs = []
if 'request' in self.context:
lang_param = self.context['request'].query_params.get('lang', None)
requested_langs = lang_param.split(',') if lang_param else []
for f in fields:
if f not in trans_fields:
all_fields.append(f)
else:
for l in settings.LANGUAGES:
if not requested_langs or l[0] in requested_langs:
all_fields.append("{}_{}".format(f, l[0]))
return all_fields
I assume this is not compatible with newer versions of DRF and Django? I get an error when running this:
File "/usr/local/lib/python3.8/site-packages/rest_framework/serializers.py", line 1038, in get_fields
field_names = self.get_field_names(declared_fields, info)
File "/code/ddp/utils/translatedmodelserializer.py", line 18, in get_field_names
if f not in trans_fields:
TypeError: argument of type 'NoneType' is not iterable
Here's a simple implementation of the above (mixin renamed). By default, it will provide all translations of the serializer fields. However, if the language get parameter is provided to the request as a comma delimited string, then only those specified translations will be returned.
from django.conf import settings from modeltranslation.manager import get_translatable_fields_for_model from rest_framework import serializers class TranslatedModelSerializerMixin(serializers.ModelSerializer): def get_field_names(self, declared_fields, info): fields = super().get_field_names(declared_fields, info) trans_fields = get_translatable_fields_for_model(self.Meta.model) all_fields = [] requested_langs = [] if 'request' in self.context: lang_param = self.context['request'].query_params.get('lang', None) requested_langs = lang_param.split(',') if lang_param else [] for f in fields: if f not in trans_fields: all_fields.append(f) else: for l in settings.LANGUAGES: if not requested_langs or l[0] in requested_langs: all_fields.append("{}_{}".format(f, l[0])) return all_fields
Here is an up to date version of this serializer :
class TranslationModelSerializer(serializers.ModelSerializer):
def get_field_names(self, declared_fields, info):
fields = super().get_field_names(declared_fields, info)
lang = (
self.context.get("request").LANGUAGE_CODE
if self.context.get("request")
else None
)
if lang:
trans_fields = get_translatable_fields_for_model(self.Meta.model)
result = []
for f in fields:
if f not in trans_fields:
result.append(f)
else:
result.append("{}_{}".format(f, lang))
else:
result = fields
return result
Depending on how your serializer is instantiated, you might need to add the request to your serializer.
I think it might make sense to commit this serializer to the repository, but it should be a little bit more adaptable.
EDIT : However serialization translation seems to work out of the box, so you should not need this class.
I used this as base an extended it, with support to read_only_fields. Isn't really "dynamic", but may be useful in some contexts.
from modeltranslation.utils import (
build_localized_fieldname,
get_translation_fields,
)
from modeltranslation.manager import (
get_translatable_fields_for_model,
)
class TranslatableModelSerializerMixin(object):
# TODO: Support exclude and __ALL__ field option
def get_field_names(self, declared_fields, info):
fields = super().get_field_names( declared_fields, info)
model_translatable_fields = get_translatable_fields_for_model(self.Meta.model)
for field in model_translatable_fields:
if field in fields:
fields += tuple(get_translation_fields(field))
return fields
def get_extra_kwargs(self):
"""
Return a dictionary mapping field names to a dictionary of
additional keyword arguments.
This class extends the original method to add tranlated fields
to read_only_fields if the original field is read_only
"""
extra_kwargs = super().get_extra_kwargs()
read_only_fields = getattr(self.Meta, 'read_only_fields', None)
if read_only_fields is not None:
model_translatable_fields = get_translatable_fields_for_model(self.Meta.model)
for field_name in read_only_fields:
if field_name in model_translatable_fields:
for translation_field_name in get_translation_fields(field_name):
kwargs = extra_kwargs.get(translation_field_name, {})
kwargs['read_only'] = True
extra_kwargs[translation_field_name] = kwargs
return extra_kwargs