Skip to content

Instantly share code, notes, and snippets.

@ishakoktn
Created May 23, 2022 13:06
Show Gist options
  • Save ishakoktn/419ae900311566f5d7cf141c2ad89f1b to your computer and use it in GitHub Desktop.
Save ishakoktn/419ae900311566f5d7cf141c2ad89f1b to your computer and use it in GitHub Desktop.
Accept Base64 Image with Django Parler Rest
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