Last active
February 18, 2016 09:46
-
-
Save vdboor/a4629d7f3d4158f3a229 to your computer and use it in GitHub Desktop.
USE https://github.com/edoburu/django-parler-rest/ NOW! THIS GIST IS DEPRECATED. It was a test to share code before making the package ----- Combining django-parler with django-rest-framework. Please tell me how that code is working for you, and if you have any improvements. The final version could be included into a 'contrib' folder of django-p…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.core.exceptions import ImproperlyConfigured | |
from rest_framework import serializers | |
from rest_framework.serializers import SortedDictWithMetadata | |
from .utils import create_translated_fields_serializer | |
class TranslatedFieldsField(serializers.WritableField): | |
""" | |
Exposing translated fields for a TranslatableModel in REST style. | |
""" | |
def __init__(self, *args, **kwargs): | |
self.serializer_class = kwargs.pop('serializer_class', None) | |
self.shared_model = kwargs.pop('shared_model', None) | |
super(TranslatedFieldsField, self).__init__(*args, **kwargs) | |
def initialize(self, parent, field_name): | |
super(TranslatedFieldsField, self).initialize(parent, field_name) | |
self._serializers = {} | |
# Expect 1-on-1 for now. | |
related_name = field_name | |
# This could all be done in __init__(), but by moving the code here, | |
# it's possible to auto-detect the parent model. | |
if self.shared_model is not None and self.serializer_class is not None: | |
return | |
# Fill in the blanks | |
if self.serializer_class is None: | |
# Auto detect parent model | |
if self.shared_model is None: | |
self.shared_model = self.parent.opts.model | |
# Create serializer based on shared model. | |
translated_model = self.shared_model._parler_meta[related_name] | |
self.serializer_class = create_translated_fields_serializer(self.shared_model, related_name=related_name, meta=dict( | |
fields = translated_model.get_translated_fields() | |
)) | |
else: | |
self.shared_model = self.serializer_class.Meta.model | |
# Don't need to have a 'language_code', it will be split up already, | |
# so this should avoid redundant output. | |
if 'language_code' in self.serializer_class.base_fields: | |
raise ImproperlyConfigured("Serializer may not have a 'language_code' field") | |
def to_native(self, value): | |
""" | |
Serialize to REST format. | |
""" | |
if value is None: | |
return None | |
# Only need one serializer to create the native objects | |
serializer = self.serializer_class() | |
# Split into a dictionary per language | |
ret = SortedDictWithMetadata() | |
for translation in value.all(): | |
ret[translation.language_code] = serializer.to_native(translation) | |
return ret | |
def from_native(self, data, files=None): | |
""" | |
Deserialize primitives -> objects. | |
""" | |
self._errors = {} | |
self._serializers = {} | |
if data is None: | |
return None | |
elif isinstance(data, dict): | |
# Very similar code to ModelSerializer.from_native(): | |
translations = self.restore_fields(data, files) | |
if translations is not None: | |
translations = self.perform_validation(translations) | |
else: | |
raise serializers.ValidationError(self.error_messages['invalid']) | |
if not self._errors: | |
return translations | |
# No 'master' object known yet, can't store fields. | |
#return self.restore_object(translations) | |
def restore_fields(self, data, files): | |
translations = {} | |
for lang_code, model_fields in data.iteritems(): | |
# Create a serializer per language, so errors can be stored per serializer instance. | |
self._serializers[lang_code] = serializer = self.serializer_class() | |
serializer._errors = {} # because it's .from_native() is skipped. | |
translations[lang_code] = serializer.restore_fields(model_fields, files) | |
return translations | |
def perform_validation(self, data): | |
# Runs `validate_<fieldname>()` and `validate()` methods on the serializer | |
for lang_code, model_fields in data.iteritems(): | |
self._serializers[lang_code].perform_validation(model_fields) | |
return data | |
# def restore_object(self, data): | |
# master = self.parent.object | |
# for lang_code, model_fields in data.iteritems(): | |
# translation = master._get_translated_model(lang_code, auto_create=True) | |
# self._serializers[lang_code].restore_object(model_fields, instance=translation) | |
def validate(self, data): | |
super(TranslatedFieldsField, self).validate(data) # checks 'required' state. | |
for lang_code, model_fields in data.iteritems(): | |
self._serializers[lang_code].validate(model_fields) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework import serializers | |
from .fields import TranslatedFieldsField | |
from myapp.modes import Region | |
class TranslatableModelSerializer(serializers.ModelSerializer): | |
""" | |
Serializer that makes sure that translations | |
from the :class:`TranslatedFieldsField` are properly saved. | |
""" | |
def save_object(self, obj, **kwargs): | |
""" | |
Extract the translations, store these into the django-parler model data. | |
""" | |
for meta in obj._parler_meta: | |
translations = obj._related_data.pop(meta.rel_name, {}) | |
if translations: | |
for lang_code, model_fields in translations.iteritems(): | |
translations = obj._get_translated_model(lang_code, auto_create=True, meta=meta) | |
for field, value in model_fields.iteritems(): | |
setattr(translations, field, value) | |
return super(TranslatableModelSerializer, self).save_object(obj, **kwargs) | |
class RegionSerializer(TranslatableModelSerializer): | |
""" | |
A model with translated fields. | |
""" | |
translations = TranslatedFieldsField(shared_model=Region) | |
class Meta: | |
model = Region | |
fields = ('id', 'foo', 'bar', 'translations',) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from rest_framework import serializers | |
def create_translated_fields_serializer(shared_model, meta=None, related_name=None, **fields): | |
""" | |
Create a REST framework serializer class for a translated fields model. | |
:param shared_model: The shared model. | |
:type shared_model: :class:`parler.models.TranslatableModel` | |
""" | |
if not related_name: | |
translated_model = shared_model._parler_meta.root_model | |
else: | |
translated_model = shared_model._parler_meta[related_name].model | |
# Define inner Meta class | |
if not meta: | |
meta = {} | |
meta['model'] = translated_model | |
meta.setdefault('fields', ['language_code'] + translated_model.get_translated_fields()) | |
# Define serialize class attributes | |
attrs = {} | |
attrs.update(fields) | |
attrs['Meta'] = type('Meta', (), meta) | |
# Dynamically create the serializer class | |
return type('{0}Serializer'.format(translated_model.__name__), (serializers.ModelSerializer,), attrs) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@grudelsud: you can use https://github.com/edoburu/django-parler-rest/ now, this code was used as basis to create the package.