Search code examples
pythondjangopython-3.xdjango-rest-frameworkdynamic-class

Issues with django rest framework using dynamic serializer


I'm have a dynamic dataset I am trying to use with the Django REST Framework by dynamically creating serializer, but it doesn't seem to process the dynamic attributes I add.

Here is the code:

-- views.py
class DynamicReadings(generics.ListAPIView):
    def get_serializer_class(self):
        site = self.kwargs['site']
        devices = Device.objects.filter(reader__site__slug=site).order_by('code')
        dyn_fields = ['x%s' % a.code for a in devices]
        return SerializerClassFactory(dyn_fields)

    def get_queryset(self):
        ...
        # the query is correct and returns data

-- serializers.py
def SerializerClassFactory(dyn_fields):
class DynamicSerializer(serializers.Serializer):
    read_at = serializers.DateTimeField()

    class Meta:
        fields = ('read_at')

    for f in dyn_fields:
        setattr(DynamicSerializer, f, serializers.FloatField())
    DynamicSerializer.Meta.fields += ','.join("'%s'" % f for f in dyn_fields)

    return DynamicSerializer

When I execute the code, only the attribute (read_at) specified in the class definition is serialized. None of the dynamic attributes appear to be working.

Example:

[{"read_at":"2017-05-07T00:12:29Z"},{"read_at":"2017-05-08T00:12:30Z"}]

Here is the output of the class after creation in shell:

>>> serializer = SerializerClassFactory(['x01', 'x02', 'x03'])
>>> serializer.__dict__
mappingproxy({'__module__': 'xxx.api.serializers', 
'Meta': <class 'xxx.api.serializers.SerializerClassFactory.<locals>.DynamicSerializer.Meta'>, 
'x02': FloatField(), '_declared_fields': OrderedDict([('read_at', DateTimeField())]), 
'__doc__': None, 'x01': FloatField(), 'x03': FloatField()})

Additionally, I've tried:

setattr(DynamicSerializer, f, property(serializers.FloatField()))
setattr(DynamicSerializer, f, type(serializers.FloatField()))

I'm not sure what I am doing wrong.


Solution

  • Here, only the fields are dynamic, the base serializer is the same. In order to make it work, we will change only the fields on the DynamicSerializer

    in serializers.py you would have:

    class DynamicSerializer(serializers.Serializer):
        read_at = serializers.DateTimeField()
    
        class Meta:
            fields = ('read_at')
    
        def __init__(*args, dyn_fields=None, **kwargs):
            dyn_fields = dyn_fields or []
            for field in dyn_fields:
                self.fields[field] = serializers.FloatField()
    
            super(DynamicSerializer, self).__init__(*args, **kwargs)
    

    and in the views.py on the get_serializer_class method you would return DynamicSerializer(dyn_fields=dyn_fields)

    for more info visit http://www.django-rest-framework.org/api-guide/serializers/#dynamically-modifying-fields