Commit 1e16cff6 by Raphael Lullis

Improved _to_native method, added a few tests for data types.

parent 19b1163b
...@@ -39,19 +39,30 @@ class ParseType(object): ...@@ -39,19 +39,30 @@ class ParseType(object):
return native and native.from_native(**parse_data) or parse_data return native and native.from_native(**parse_data) or parse_data
@staticmethod @staticmethod
def convert_to_parse(python_object): def convert_to_parse(python_object, as_pointer=False):
is_object = isinstance(python_object, Object)
if is_object and not as_pointer:
return dict([(k, ParseType.convert_to_parse(v, as_pointer=True))
for k, v in python_object._editable_attrs
])
python_type = Object if is_object else type(python_object)
# classes that need to be cast to a different type before serialization
transformation_map = {
datetime.datetime: Date,
Object: Pointer
}
if python_type in transformation_map:
klass = transformation_map.get(python_type)
return klass(python_object)._to_native()
if isinstance(python_object, ParseType): if isinstance(python_object, ParseType):
return python_object._to_native() return python_object._to_native()
# This seems rather pointless now that we are only working return python_object
# with dates. Perhaps when files/images start to get a little
# more attention, we will have more things here.
python_type = type(python_object)
klass = {
datetime.datetime: Date
}.get(python_type)
return klass(python_object)._to_native() if klass else python_object
@classmethod @classmethod
def from_native(cls, **kw): def from_native(cls, **kw):
...@@ -68,11 +79,14 @@ class Pointer(ParseType): ...@@ -68,11 +79,14 @@ class Pointer(ParseType):
klass = Object.factory(kw.get('className')) klass = Object.factory(kw.get('className'))
return klass.retrieve(kw.get('objectId')) return klass.retrieve(kw.get('objectId'))
def __init__(self, obj):
self._object = obj
def _to_native(self): def _to_native(self):
return { return {
'__type': 'Pointer', '__type': 'Pointer',
'className': self.__class__.__name__, 'className': self._object.__class__.__name__,
'objectId': self.objectId 'objectId': self._object.objectId
} }
...@@ -143,11 +157,13 @@ class File(ParseType): ...@@ -143,11 +157,13 @@ class File(ParseType):
@classmethod @classmethod
def from_native(cls, **kw): def from_native(cls, **kw):
return cls(kw.get('name')) return cls(**kw)
def __init__(self, name): def __init__(self, **kw):
name = kw.get('name')
self._name = name self._name = name
self._url = '/'.join([API_ROOT, 'files', name]) self._api_url = '/'.join([API_ROOT, 'files', name])
self._file_url = kw.get('url')
def _to_native(self): def _to_native(self):
return { return {
...@@ -155,7 +171,10 @@ class File(ParseType): ...@@ -155,7 +171,10 @@ class File(ParseType):
'name': self._name 'name': self._name
} }
url = property(lambda self: self._url) url = property(lambda self: self._file_url)
name = property(lambda self: self._name)
_absolute_url = property(lambda self: self._api_url)
class ParseResource(ParseBase, Pointer): class ParseResource(ParseBase, Pointer):
...@@ -165,37 +184,35 @@ class ParseResource(ParseBase, Pointer): ...@@ -165,37 +184,35 @@ class ParseResource(ParseBase, Pointer):
def retrieve(cls, resource_id): def retrieve(cls, resource_id):
return cls(**cls.GET('/' + resource_id)) return cls(**cls.GET('/' + resource_id))
@property
def _editable_attrs(self):
protected_attrs = self.__class__.PROTECTED_ATTRIBUTES
allowed = lambda a: a not in protected_attrs and not a.startswith('_')
return [(k, v) for k, v in self.__dict__.items() if allowed(k)]
def __init__(self, **kw): def __init__(self, **kw):
for key, value in kw.items(): for key, value in kw.items():
setattr(self, key, ParseType.convert_from_parse(value)) setattr(self, key, ParseType.convert_from_parse(value))
def _to_dict(self): def _to_native(self):
# serializes all attributes that need to be persisted on Parse return ParseType.convert_to_parse(self)
protected_attributes = self.__class__.PROTECTED_ATTRIBUTES
is_protected = lambda a: a in protected_attributes or a.startswith('_')
return dict([(k, ParseType.convert_to_parse(v))
for k, v in self.__dict__.items() if not is_protected(k)
])
def _get_object_id(self): def _get_object_id(self):
return getattr(self, '_object_id', None) return self.__dict__.get('_object_id')
def _set_object_id(self, value): def _set_object_id(self, value):
if hasattr(self, '_object_id'): if '_object_id' in self.__dict__:
raise ValueError('Can not re-set object id') raise ValueError('Can not re-set object id')
self._object_id = value self._object_id = value
def _get_updated_datetime(self): def _get_updated_datetime(self):
return getattr(self, '_updated_at', None) and self._updated_at._date return self.__dict__.get('_updated_at') and self._updated_at._date
def _set_updated_datetime(self, value): def _set_updated_datetime(self, value):
self._updated_at = Date(value) self._updated_at = Date(value)
def _get_created_datetime(self): def _get_created_datetime(self):
return getattr(self, '_created_at', None) and self._created_at._date return self.__dict__.get('_created_at') and self._created_at._date
def _set_created_datetime(self, value): def _set_created_datetime(self, value):
self._created_at = Date(value) self._created_at = Date(value)
...@@ -207,21 +224,14 @@ class ParseResource(ParseBase, Pointer): ...@@ -207,21 +224,14 @@ class ParseResource(ParseBase, Pointer):
self._create() self._create()
def _create(self): def _create(self):
# URL: /1/classes/<className>
# HTTP Verb: POST
uri = self.__class__.ENDPOINT_ROOT uri = self.__class__.ENDPOINT_ROOT
response_dict = self.__class__.POST(uri, **self._to_native())
response_dict = self.__class__.POST(uri, **self._to_dict())
self.createdAt = self.updatedAt = response_dict['createdAt'] self.createdAt = self.updatedAt = response_dict['createdAt']
self.objectId = response_dict['objectId'] self.objectId = response_dict['objectId']
def _update(self): def _update(self):
# URL: /1/classes/<className>/<objectId> response = self.__class__.PUT(self._absolute_url, **self._to_native())
# HTTP Verb: PUT
response = self.__class__.PUT(self._absolute_url, **self._to_dict())
self.updatedAt = response['updatedAt'] self.updatedAt = response['updatedAt']
def delete(self): def delete(self):
...@@ -269,11 +279,16 @@ class Object(ParseResource): ...@@ -269,11 +279,16 @@ class Object(ParseResource):
@property @property
def _absolute_url(self): def _absolute_url(self):
if not self.objectId: if not self.objectId: return None
return None
return '/'.join([self.__class__.ENDPOINT_ROOT, self.objectId]) return '/'.join([self.__class__.ENDPOINT_ROOT, self.objectId])
@property
def as_pointer(self):
return Pointer(**{
'className': self.__class__.__name__,
'objectId': self.objectId
})
def increment(self, key, amount=1): def increment(self, key, amount=1):
""" """
Increment one value in the object. Note that this happens immediately: Increment one value in the object. Note that this happens immediately:
......
...@@ -132,17 +132,52 @@ class TestObject(unittest.TestCase): ...@@ -132,17 +132,52 @@ class TestObject(unittest.TestCase):
"""test saving and associating a different object""" """test saving and associating a different object"""
collectedItem = CollectedItem(type="Sword", isAwesome=True) collectedItem = CollectedItem(type="Sword", isAwesome=True)
collectedItem.save() collectedItem.save()
self.score.item = collectedItem self.score.item = collectedItem
self.score.save() self.score.save()
# get the object, see if it has saved # get the object, see if it has saved
qs = GameScore.Query.get(objectId=self.score.objectId) qs = GameScore.Query.get(objectId=self.score.objectId)
self.assert_(isinstance(qs.item, Object), self.assert_(isinstance(qs.item, Object),
"Associated CollectedItem is not of correct class") "Associated CollectedItem is not an object")
self.assert_(qs.item.type == "Sword", self.assert_(qs.item.type == "Sword",
"Associated CollectedItem does not have correct attributes") "Associated CollectedItem does not have correct attributes")
class TestTypes(unittest.TestCase):
def setUp(self):
self.now = datetime.datetime.now()
self.score = GameScore(
score=1337, player_name='John Doe', cheat_mode=False,
date_of_birth=self.now
)
self.sao_paulo = City(
name='São Paulo', location=GeoPoint(-23.5, -46.6167)
)
def testCanConvertToNative(self):
native_data = self.sao_paulo._to_native()
self.assert_(type(native_data) is dict, 'Can not convert object to dict')
def testCanConvertNestedLocation(self):
native_sao_paulo = self.sao_paulo._to_native()
location_dict = native_sao_paulo.get('location')
self.assert_(type(location_dict) is dict,
'Expected dict after conversion. Got %s' % location_dict)
self.assert_(location_dict.get('latitude') == -23.5,
'Can not serialize geopoint data')
def testCanConvertDate(self):
native_score = self.score._to_native()
native_date = self.score._to_native().get('date_of_birth')
self.assert_(type(native_date) is dict,
'Could not serialize date into dict')
iso_date = native_date.get('iso')
self.assert_(iso_date == self.now.isoformat(),
'Expected %s. Got %s' % (self.now.isoformat(), iso_date))
class TestQuery(unittest.TestCase): class TestQuery(unittest.TestCase):
"""Tests of an object's Queryset""" """Tests of an object's Queryset"""
def setUp(self): def setUp(self):
......
...@@ -13,9 +13,10 @@ ...@@ -13,9 +13,10 @@
import base64 import base64
import datetime import datetime
import copy
from __init__ import API_ROOT from __init__ import API_ROOT
from datatypes import ParseResource from datatypes import ParseResource, ParseType
from query import QueryManager from query import QueryManager
...@@ -57,7 +58,7 @@ class User(ParseResource): ...@@ -57,7 +58,7 @@ class User(ParseResource):
return self.__class__.PUT( return self.__class__.PUT(
self._absolute_url, self._absolute_url,
extra_headers=session_header, extra_headers=session_header,
**self._to_dict()) **self._to_native())
@login_required @login_required
def delete(self): def delete(self):
...@@ -87,6 +88,10 @@ class User(ParseResource): ...@@ -87,6 +88,10 @@ class User(ParseResource):
except Exception, why: except Exception, why:
return False return False
def _to_native(self):
return dict([(k, ParseType.convert_to_parse(v, as_pointer=True))
for k, v in self._editable_attrs])
def __repr__(self): def __repr__(self):
return '<User:%s (Id %s)>' % (self.username, self.objectId) return '<User:%s (Id %s)>' % (self.username, self.objectId)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment