Created
May 23, 2022 13:06
-
-
Save ishakoktn/419ae900311566f5d7cf141c2ad89f1b to your computer and use it in GitHub Desktop.
Accept Base64 Image with Django Parler Rest
This file contains 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 django.db import models | |
from drf_extra_fields.fields import Base64ImageField | |
from parler.models import TranslatableModelMixin, TranslatedFieldsModel | |
from parler_rest.serializers import TranslatedFieldsField | |
class Base64ImageModelMixin: | |
serializer_field_mapping = { | |
models.AutoField: serializers.IntegerField, | |
models.BigIntegerField: serializers.IntegerField, | |
models.BooleanField: serializers.BooleanField, | |
models.CharField: serializers.CharField, | |
models.CommaSeparatedIntegerField: serializers.CharField, | |
models.DateField: serializers.DateField, | |
models.DateTimeField: serializers.DateTimeField, | |
models.DecimalField: serializers.DecimalField, | |
models.DurationField: serializers.DurationField, | |
models.EmailField: serializers.EmailField, | |
models.Field: serializers.ModelField, | |
models.FileField: serializers.FileField, | |
models.FloatField: serializers.FloatField, | |
models.ImageField: Base64ImageField, # this line changed | |
models.IntegerField: serializers.IntegerField, | |
models.NullBooleanField: serializers.BooleanField, | |
models.PositiveIntegerField: serializers.IntegerField, | |
models.PositiveSmallIntegerField: serializers.IntegerField, | |
models.SlugField: serializers.SlugField, | |
models.SmallIntegerField: serializers.IntegerField, | |
models.TextField: serializers.CharField, | |
models.TimeField: serializers.TimeField, | |
models.URLField: serializers.URLField, | |
models.UUIDField: serializers.UUIDField, | |
models.GenericIPAddressField: serializers.IPAddressField, | |
models.FilePathField: serializers.FilePathField, | |
} | |
def create_base64image_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__), | |
(Base64ImageModelMixin, serializers.ModelSerializer), | |
attrs, | |
) | |
class Base64ImageTranslatedFieldsField(TranslatedFieldsField): | |
def bind(self, field_name, parent): | |
""" | |
Create translation serializer dynamically. | |
Takes translatable model class (shared_model) from parent serializer and it | |
may create a serializer class on the fly if no custom class was specified. | |
""" | |
super(TranslatedFieldsField, self).bind(field_name, parent) | |
# Expect 1-on-1 for now. Allow using source as alias, | |
# but it should not be a dotted path for now | |
related_name = self.source or 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: | |
if self.shared_model is None: | |
# Auto detect parent model | |
from .serializers import TranslatableModelSerializerMixin | |
if not isinstance(parent, TranslatableModelSerializerMixin): | |
raise TypeError( | |
"Expected 'TranslatableModelSerializer' as serializer base class" | |
) | |
if not issubclass(parent.Meta.model, TranslatableModelMixin): | |
raise TypeError( | |
"Expected 'TranslatableModel' for the parent model" | |
) | |
self.shared_model = parent.Meta.model | |
# Create serializer based on shared model. | |
translated_model = self.shared_model._parler_meta[related_name] | |
self.serializer_class = ( | |
create_base64image_translated_fields_serializer( | |
self.shared_model, | |
related_name=related_name, | |
meta={'fields': translated_model.get_translated_fields()}, | |
) | |
) | |
else: | |
if not issubclass( | |
self.serializer_class.Meta.model, TranslatedFieldsModel | |
): | |
raise TypeError( | |
"Expected 'TranslatedFieldsModel' for the serializer model" | |
) | |
def to_internal_value(self, data): | |
""" | |
Because of parent does not implement paritial update we ovveride this method | |
""" | |
if data is None: | |
return | |
if not isinstance(data, dict): | |
self.fail('invalid') | |
if not self.allow_empty and len(data) == 0: | |
self.fail('empty') | |
result, errors = {}, {} | |
for lang_code, model_fields in data.items(): | |
serializer = self.serializer_class( | |
data=model_fields, | |
context=self.context, | |
partial=self.parent.partial, | |
) | |
if serializer.is_valid(): | |
result[lang_code] = serializer.validated_data | |
else: | |
errors[lang_code] = serializer.errors | |
if errors: | |
raise serializers.ValidationError(errors) | |
return result | |
# Usage | |
class MySerializer(TranslatableModelSerializer): | |
translations = Base64ImageTranslatedFieldsField(shared_model=MyModel) | |
class Meta: | |
model = MyModel | |
fields = '__all__' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment