clarkduvall / serpy

ridiculously fast object serialization
http://serpy.readthedocs.org/en/latest/
MIT License
960 stars 62 forks source link

Serializing properties which may or may not be present in object being passed in #54

Closed JoelBennett closed 7 years ago

JoelBennett commented 7 years ago

Forgive me if my terminology is a bit off. So far I've been pretty impressed with Serpy - we were able to get some serious performance out of it compared to the Django Rest Framework serializer when returning several thousand objects in a single call (don't ask).

I have run into a small challenge, and I'm not quite sure the best way to overcome it. I've got a Serializer that inherits from serpy.Serializer, which has a number of fields (let's call them a, b, c, d, etc.). These fields may or may not be present in the object which I need to serialize, but I'd always like to make sure that they are present with a value of None in the serialized data. So my serializer looks something like:

class MySerializer(serpy.Serializer):
    a = serpy.FloatField()
    b = serpy.FloatField()
    c = serpy.FloatField()
    # ... and so on, for a bunch of fields

Inside of a Django view, the QuerySet of objects is filtered, and passed into the serializer.

# someObjects is a QuerySet of objects, which may or may not contain a, b, c, etc.
serializer = MySerializer(someObjects, many=True)

This raises the following exception:


... views.py", line 80, in get
    return Response(serializer.data)
  File "C:\Python27\lib\site-packages\serpy\serializer.py", line 139, in data
    self._data = self.to_value(self.instance)
  File "C:\Python27\lib\site-packages\serpy\serializer.py", line 128, in to_value
    return [serialize(o, fields) for o in instance]
  File "C:\Python27\lib\site-packages\serpy\serializer.py", line 116, in _serialize
    result = to_value(result)
TypeError: float() argument must be a string or a number```
Switching the FloatFields to be required=False no longer raises the exception, but it no longer puts the corresponding dictionary key in the output.  Ideally I would like the value of a, b, c, etc. to always be present in the output but with a value of None.  Is there a way to do this en-mass, without having to do something for every field I have that is like this?
clarkduvall commented 7 years ago

There's not a great way to do this on the field itself, but you could override the to_value method in your custom serializer like this (code not tested but idea should work):

class MySerializer(serpy.Serializer):
    def set_defaults(self, v):
        for name, _, _, _, _, _ in self._compiled_fields:
            v.setdefault(name)
        return v

    def to_value(self, instance):
        v = super(MySerializer, self).to_value(instance)
        if self.many:
            return map(self.set_defaults, v)
        return self.set_defaults(v)

Let me know if that will solve your issue.

JoelBennett commented 7 years ago

That actually worked perfectly. Thanks!