Skip to content

Instantly share code, notes, and snippets.

@roman-karpovich
Created April 25, 2018 12:17
Show Gist options
  • Save roman-karpovich/99dcc56a612dfbfbe0edfb58dd8f0753 to your computer and use it in GitHub Desktop.
Save roman-karpovich/99dcc56a612dfbfbe0edfb58dd8f0753 to your computer and use it in GitHub Desktop.
Serializer field for generic
class GenericWriteField(serializers.JSONField):
"""
Field for using generics in serializers. Input object structure:
{
"app_module": application_name,
"model": model_name,
"pk": pk
}
"""
allowed_models = None
def __init__(self, *args, **kwargs):
self.allowed_models = kwargs.pop('allowed_models', None)
super(GenericWriteField, self).__init__(*args, **kwargs)
def to_internal_value(self, data):
app_module = data.get('app_module')
model = data.get('model')
pk = data.get('pk')
if not app_module or not model or not pk:
raise serializers.ValidationError('Invalid object structure')
try:
model_class = apps.get_model(app_module, model)
except LookupError:
raise serializers.ValidationError('Invalid relation')
if self.allowed_models and not any(map(lambda m: issubclass(model_class, m), self.allowed_models)):
raise serializers.ValidationError('Unsupported model')
try:
instance = model_class.objects.get(pk=pk)
except model_class.DoesNotExist:
raise serializers.ValidationError('No such object')
return instance
def to_representation(self, value):
return {
'app_module': value._meta.app_label,
'model': value._meta.label,
'pk': value.pk
}
class GenericReadField(serializers.ReadOnlyField):
serializers_mapping = {}
def __init__(self, **kwargs):
self.serializers_mapping = kwargs.pop('serializers_mapping', self.serializers_mapping)
super(GenericReadField, self).__init__(**kwargs)
def to_representation(self, value):
data = {
'app_module': value._meta.app_label,
'model': value._meta.model_name,
'pk': value.pk,
'str': six.text_type(value)
}
if hasattr(value, 'get_object_url'):
data['url'] = value.get_object_url()
for model, serializer in self.serializers_mapping.items():
if isinstance(value, model):
data['data'] = serializer(instance=value, context=self.context).data
break
return data
class GenericField(SeparatedReadWriteField):
def __init__(self, serializers_mapping, *args, **kwargs):
read_field_kwargs = ['label', ]
write_field_kwargs = ['required', ]
read_field = GenericReadField(
serializers_mapping=serializers_mapping,
**{key: kwargs.get(key) for key in read_field_kwargs if key in kwargs}
)
kwargs['write_field'] = GenericWriteField(
allowed_models=serializers_mapping.keys(), write_only=True,
**{key: kwargs.get(key) for key in write_field_kwargs if key in kwargs}
)
super(GenericField, self).__init__(read_field, *args, **kwargs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment