Skip to content

Commit

Permalink
Allow provision for ordering fields when setting them.
Browse files Browse the repository at this point in the history
When deserializing Django objects, reverse relations need to be deserialized after primary key field

Fixes clarkduvall#3
  • Loading branch information
Aniruddha Maru committed Jun 11, 2015
1 parent fcb1cf7 commit 1f6d4e3
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 5 deletions.
30 changes: 25 additions & 5 deletions serpy/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,32 @@ def _compile_meta(direct_fields, serializer_meta, serializer_cls):
# get the right order of meta bases
meta_bases = meta_bases[::-1]

# automatically create an inner-class Meta that inherits from
# parent class's inner-class Meta
Meta = type('Meta', meta_bases, {})
meta = Meta()

compiled_read_fields = [
_compile_read_field_to_tuple(field, name, serializer_cls)
for name, field in field_map.items()
]

field_map_items = []
if not meta.override_deser_field_order:
field_map_items = field_map.items()
else:
for field in meta.override_deser_field_order:
field_map_items.append((field, field_map[field]))
for name, field in field_map.items():
if name not in meta.override_deser_field_order:
field_map_items.append((name, field))

compiled_write_fields = [
_compile_write_field_to_tuple(field, name, serializer_cls)
for name, field in field_map.items()
for name, field in field_map_items
if not field.read_only
]

# automatically create an inner-class Meta that inherits from
# parent class's inner-class Meta
Meta = type('Meta', meta_bases, {})
meta = Meta()
meta._field_map = field_map
meta._compiled_read_fields = compiled_read_fields
meta._compiled_write_fields = compiled_write_fields
Expand Down Expand Up @@ -142,10 +153,19 @@ class FooSerializer(Serializer):
"""
# Inner-class
class Meta(object):
# class of the deserialized object
cls = None

# default getter to use for getting field values
default_getter = operator.attrgetter

# default setter to use for setting field values
default_setter = attrsetter

# override some fields to be ordered before others
# can also be used to order all fields
override_deser_field_order = []

def __init__(self, instance=None, data=None, many=False, **kwargs):
super(Serializer, self).__init__(**kwargs)
self._can_serialize = instance is not None
Expand Down
31 changes: 31 additions & 0 deletions tests/test_serializer.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import collections
import unittest

from serpy.fields import Field, MethodField, IntField, FloatField, StrField
Expand Down Expand Up @@ -367,6 +368,36 @@ class Meta:
self.assertRaises(AttributeError,
lambda: serializer.deserialized_value)

def test_serializer_field_ordering(self):
class OrderedDictObj(collections.OrderedDict):
def __setattr__(self, name, value):
if not name.startswith('_'):
self[name] = value
else:
super(OrderedDictObj, self).__setattr__(name, value)

class ASerializer(Serializer):
class Meta:
cls = OrderedDictObj
override_deser_field_order = ['a']

a = IntField()
b = IntField()

class BSerializer(Serializer):
class Meta:
cls = OrderedDictObj
override_deser_field_order = ['b']

a = IntField()
b = IntField()

data = {'a': 1, 'b': 2}
a_obj = ASerializer(data=data).deserialized_value
b_obj = BSerializer(data=data).deserialized_value

self.assertEqual(a_obj.keys(), ['a', 'b'])
self.assertEqual(b_obj.keys(), ['b', 'a'])

if __name__ == '__main__':
unittest.main()

0 comments on commit 1f6d4e3

Please sign in to comment.