serpy icon indicating copy to clipboard operation
serpy copied to clipboard

Add support for deserialization

Open maroux opened this issue 10 years ago • 21 comments

  • Added support for read-only fields
  • Renamed .data to .representation (and added .internal_value) for better clarity
  • Renamed .to_value to .to_representation (and added to_internal_value) for better clarity

TBD update documentation, fix benchmarks, update benchmarks

Fixes clarkduvall/serpy#1

maroux avatar Jun 03 '15 18:06 maroux

thanks for the pr! will take a look tonight at it

clarkduvall avatar Jun 03 '15 19:06 clarkduvall

Coverage Status

Coverage remained the same at 100.0% when pulling 6d7fcafc0371eea072859a1ef90fed1f71bf0c84 on maroux:deserialization into 3663aafd10f86b27645fd62a7a00a64f692f4a13 on clarkduvall:master.

coveralls avatar Jun 03 '15 19:06 coveralls

quick high level comment: I like the idea of having a similar API to DRF serializers using .to_representation and .to_internal_value now that both serialization and deserialization is supported. Along with that, I would like to keep .data instead of .representation, since it will be an easier drop in replacement for DRF.

clarkduvall avatar Jun 03 '15 19:06 clarkduvall

It looks like DRF uses update/create/save for deserialization: http://www.django-rest-framework.org/api-guide/serializers/#saving-instances and Marshmallow uses make_object: http://marshmallow.readthedocs.org/en/latest/quickstart.html#deserializing-to-objects

I'm thinking something like that would be nicer than the _cls class variable. What do you think of a make_object method like Marshmallow?

clarkduvall avatar Jun 04 '15 05:06 clarkduvall

would be cool to add some benchmarks comparing the deserialization speed to DRF and Marshmallow, but that doesn't have to be in this PR

clarkduvall avatar Jun 04 '15 05:06 clarkduvall

since it will be an easier drop in replacement for DRF.

Anything that'd make it easy to use as an alternative serializer implementation for REST framework would get a big +1 from me :smile:

tomchristie avatar Jun 04 '15 09:06 tomchristie

@clarkduvall @tomchristie Agreed that interface should be a drop-in replacement of DRF.

How about:

Serializer(instance=obj).data and Serializer(data=data).instance (this has to be slightly different because DRF 3.0 changed to explicitly using .create(validated_data). To be fair - Serpy could also do the same .create() does deserialization, but I like the property access pattern instead of method call.

Agreed on the _cls class variable. I wasn't sure what's the best way - perhaps Meta inner class? What do you think? I'm entirely sure about the impact of that on performance - I think it's time I set up the benchmarks :)

maroux avatar Jun 09 '15 17:06 maroux

@maroux I like the Serializer(instance=obj).data and Serializer(data=data).instance

I think it would be more flexible to have a function instead of just specifying the class for deserialization in case some extra massaging of the data has to be done before returning the instance. So if we had a make_instance function that takes the serialized data you could make the .instance property something like:

def make_instance(self, data):
  return SomeClass(**data)

@property
def instance(self):
  if self._instance is None:
    self._instance = self.make_instance(self.to_internal_value(self._data))
  return self._instance

by default make_instance could just be a noop

clarkduvall avatar Jun 09 '15 17:06 clarkduvall

@clarkduvall So the problem with make_instance approach is two-fold: I think Serpy should do instance -> dict, and dict -> instance and since the default serializer is capable of taking in an object, I think it should also be able to create an object of arbitrary type.

How about this:

Serializer(instance=obj).data

and

Serializer(data=data).create(klass)

I'm not sure if there is value is setting a default value of klass to None, it would just return data as is? :/

maroux avatar Jun 09 '15 18:06 maroux

Actually, disregard that suggestion, the Serializer(data=data).create(klass) approach will break nested serializers.

maroux avatar Jun 09 '15 18:06 maroux

@clarkduvall What do you think about https://github.com/clarkduvall/serpy/issues/3?

I'm now inclined to go back to the Meta inner-class approach that will allow us customize the serializer behavior cleanly.

maroux avatar Jun 11 '15 02:06 maroux

Yeah I think as more features are added the Meta inner class will be better. Lets do that, we can also move default_getter and default_setter to the Meta class

clarkduvall avatar Jun 11 '15 04:06 clarkduvall

I still want a make_object function instead of doing cls() and setting attributes on it. I think by default the deserialized output should be a dict, and users shouldn't need to specify any class so we don't assume too much about what the final deserialized output will be. If they want it in class form, they can use the make_object method.

clarkduvall avatar Jun 12 '15 00:06 clarkduvall

@clarkduvall I'm confused, wouldn't make_object become no-op in default case then since it's input is already a dict? :/

maroux avatar Jun 12 '15 00:06 maroux

yep, by default it would be a no-op unless you override it to return a class, see marshmallows implementation: https://github.com/marshmallow-code/marshmallow/blob/c4330c80073811bc175ffead89fe3cdc91797421/marshmallow/schema.py#L587-L595

For example to override it for a Django User model:

def make_object(self, data):
    return User(**data)

then when you do serializer.deserialized_data it will return a User object

clarkduvall avatar Jun 12 '15 00:06 clarkduvall

I see what you mean. What's your thought on keeping it no-op in the default implementation, but have another serializer that lets you pass in cls?

maroux avatar Jun 12 '15 00:06 maroux

@maroux can you give me an example of what you mean?

clarkduvall avatar Jun 12 '15 00:06 clarkduvall

I just came across this great library. Deserialization would be a big win - I can't really contribute much at this point besides my gratitude.

squamous avatar Oct 01 '15 03:10 squamous

I would be really thankful if deserialization compatible with DRF is implemented.

ngaurav avatar Apr 18 '16 19:04 ngaurav

I'm using serpy in one of my projects and now I have to use DRF for deserialization and serpy for serialization. Having to define the same schema twice is rather unconvenient. Are there any plans when deserialization is going to be supported?

+1 for the make_object method rather than calling cls() as it allows for structures like {"type": "Foo", title="BlahBlah"} and {"type": "Bar", "title"="Blurb"} where the first is serialized as Foo and the second as Bar object. Keep it stupid simple and provide hooks to customize object creation. I also think that it is a good idea to pass the deserializer context to make_object. Otherwise it would be impossible to deserializer something like {"payload_type": "Foo", "payload_data": {"title": "BlahBlah"}}. That's a thing DRF does not support but Marshmallow does.

bikeshedder avatar Jul 04 '16 13:07 bikeshedder

I don't have a lot of time to work on this now, but if someone wants to implement this (or @maroux wants to continue this PR) using the make_object interface that was discussed earlier, I would definitely take a look at it.

clarkduvall avatar Jul 17 '16 17:07 clarkduvall