Serialize and de-serialize the Django Enum field to accept both numeric and textual representation

I am trying to create an enumeration field in Django that after a GET request will return a text representation of the enumeration and on a POST or PATCH request, converts the text representation into an appropriate integer before saving.

transform_<field>() 

works well for converting the integer enum value to the corresponding string, but I cannot figure out how best to convert the string to this corresponding integer, except hacking

 validate_<field>() 

method.

Is there a better way to do this? See code below

Model File

 class Status(enum.Enum): RUNNING = 0 COMPLETED = 1 labels = { RUNNING: 'Running', COMPLETED: 'Completed' } translation = {v: k for k, v in labels.iteritems()} class Job(models.Model): status = enum.EnumField(Status) 

Serializer

 class JobSeralizer(serializers.ModelSerailzer): status = seralizers.CharField(max_length=32, default=Status.QUEUED) def transform_status(self, obj, value): return JobStatus.labels[value] def validate_status(self, attrs, source): """Allow status to take numeric or character representation of status """ status = attrs[source] if status in JobStatus.translation: attrs[source] = JobStatus.translation[status] elif status.isdigit(): attrs[source] = int(status) else: raise serializers.ValidationError("'%s' not a valid status" % status) return attrs 
+7
python django django-rest-framework
source share
1 answer

As indicated by OP, you can do this easily using custom fields in drf v3.x. Here is a brief example of a common custom field used to convert label values ​​(for example, enumeration values ​​↔ text representation):

 class KeyValueField(serializers.Field): """ A field that takes a field value as the key and returns the associated value for serialization """ labels = {} inverted_labels = {} def __init__(self, labels, *args, **kwargs): self.labels = labels # Check to make sure the labels dict is reversible, otherwise # deserialization may produce unpredictable results inverted = {} for k, v in labels.iteritems(): if v in inverted: raise ValueError( 'The field is not deserializable with the given labels.' ' Please ensure that labels map 1:1 with values' ) inverted[v] = k self.inverted_labels = inverted return super(KeyValueField, self).__init__(*args, **kwargs) def to_representation(self, obj): if type(obj) is list: return [self.labels.get(o, None) for o in obj] else: return self.labels.get(obj, None) def to_internal_value(self, data): if type(data) is list: return [self.inverted_labels.get(o, None) for o in data] else: return self.inverted_labels.get(data, None) 

Field initialization will look something like this:

 class MySerializer(serializers.Serializer): afield = KeyValueField(labels={0:'enum text 0', 1:'enum text 1'}) 
+4
source share

All Articles