Commit d33a47cd by David Robinson

Fixed #9- can save Pointers to Objects

Fixed #9 by allowing ParseResources to be converted into Pointers and
back, such as when one object is set as an attribute of another. This
required:

- ParseResource now inherits Pointer
- All values are now converted to ParseType in the initialization of a
ParseResource
- The `_to_native` method of ParseResource was changed to `_to_dict`,
which allows `_to_native()` to construct a Pointer when one object is
an attribute of another.

Also:

- Added test coverage and example to README

- Fixed bug where many `from_native` methods used `self` instead of
`cls`

- `class_name` is converted to a string before being assigned to
`DerivedClass.__name__` (necessary because it is unicode when returned
from Parse)

- Changed TestUser to rely on parse_user.User instead of
parse_user.user.User
parent eaaccd92
...@@ -141,6 +141,18 @@ restaurant.location ="POINT(12.0 -34.45)" ...@@ -141,6 +141,18 @@ restaurant.location ="POINT(12.0 -34.45)"
restaurant.save() restaurant.save()
~~~~~ ~~~~~
We can store a reference to another Object by assigning it to an attribute:
~~~~~ {python}
class CollectedItem(parse_rest.Object):
pass
collectedItem = CollectedItem(type="Sword", isAwesome=True)
collectedItem.save() # we have to save it before it can be referenced
gameScore.item = collectedItem
~~~~~
Querying Querying
-------- --------
......
...@@ -107,7 +107,7 @@ class Date(ParseType): ...@@ -107,7 +107,7 @@ class Date(ParseType):
class Binary(ParseType): class Binary(ParseType):
@classmethod @classmethod
def from_native(self, **kw): def from_native(cls, **kw):
return cls(kw.get('base64', '')) return cls(kw.get('base64', ''))
def __init__(self, encoded_string): def __init__(self, encoded_string):
...@@ -121,7 +121,7 @@ class Binary(ParseType): ...@@ -121,7 +121,7 @@ class Binary(ParseType):
class GeoPoint(ParseType): class GeoPoint(ParseType):
@classmethod @classmethod
def from_native(self, **kw): def from_native(cls, **kw):
return cls(kw.get('latitude'), kw.get('longitude')) return cls(kw.get('latitude'), kw.get('longitude'))
def __init__(self, latitude, longitude): def __init__(self, latitude, longitude):
...@@ -139,7 +139,7 @@ class GeoPoint(ParseType): ...@@ -139,7 +139,7 @@ class GeoPoint(ParseType):
class File(ParseType): class File(ParseType):
@classmethod @classmethod
def from_native(self, **kw): def from_native(cls, **kw):
return cls(kw.get('url'), kw.get('name')) return cls(kw.get('url'), kw.get('name'))
def __init__(self, url, name): def __init__(self, url, name):
...@@ -211,7 +211,7 @@ class Function(ParseBase): ...@@ -211,7 +211,7 @@ class Function(ParseBase):
return self.POST("/" + self.name, **kwargs) return self.POST("/" + self.name, **kwargs)
class ParseResource(ParseBase): class ParseResource(ParseBase, Pointer):
PROTECTED_ATTRIBUTES = ['objectId', 'createdAt', 'updatedAt'] PROTECTED_ATTRIBUTES = ['objectId', 'createdAt', 'updatedAt']
...@@ -221,9 +221,9 @@ class ParseResource(ParseBase): ...@@ -221,9 +221,9 @@ class ParseResource(ParseBase):
def __init__(self, **kw): def __init__(self, **kw):
for key, value in kw.items(): for key, value in kw.items():
setattr(self, key, value) setattr(self, key, ParseType.convert(value))
def _to_native(self): def _to_dict(self):
# serializes all attributes that need to be persisted on Parse # serializes all attributes that need to be persisted on Parse
protected_attributes = self.__class__.PROTECTED_ATTRIBUTES protected_attributes = self.__class__.PROTECTED_ATTRIBUTES
...@@ -265,7 +265,7 @@ class ParseResource(ParseBase): ...@@ -265,7 +265,7 @@ class ParseResource(ParseBase):
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']
...@@ -274,7 +274,7 @@ class ParseResource(ParseBase): ...@@ -274,7 +274,7 @@ class ParseResource(ParseBase):
# URL: /1/classes/<className>/<objectId> # URL: /1/classes/<className>/<objectId>
# HTTP Verb: PUT # HTTP Verb: PUT
response = self.__class__.PUT(self._absolute_url, **self._to_native()) response = self.__class__.PUT(self._absolute_url, **self._to_dict())
self.updatedAt = response['updatedAt'] self.updatedAt = response['updatedAt']
def delete(self): def delete(self):
...@@ -306,7 +306,8 @@ class Object(ParseResource): ...@@ -306,7 +306,8 @@ class Object(ParseResource):
def factory(cls, class_name): def factory(cls, class_name):
class DerivedClass(cls): class DerivedClass(cls):
pass pass
DerivedClass.__name__ = class_name DerivedClass.__name__ = str(class_name)
DerivedClass.set_endpoint_root()
return DerivedClass return DerivedClass
@classmethod @classmethod
...@@ -366,7 +367,7 @@ class User(ParseResource): ...@@ -366,7 +367,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_native()) **self._to_dict())
@login_required @login_required
def delete(self): def delete(self):
......
...@@ -13,7 +13,6 @@ import datetime ...@@ -13,7 +13,6 @@ import datetime
import __init__ as parse_rest import __init__ as parse_rest
from __init__ import GeoPoint, Object from __init__ import GeoPoint, Object
from user import User
import query import query
...@@ -57,6 +56,10 @@ class Review(Object): ...@@ -57,6 +56,10 @@ class Review(Object):
pass pass
class CollectedItem(Object):
pass
class TestObject(unittest.TestCase): class TestObject(unittest.TestCase):
def setUp(self): def setUp(self):
self.score = GameScore( self.score = GameScore(
...@@ -116,6 +119,20 @@ class TestObject(unittest.TestCase): ...@@ -116,6 +119,20 @@ class TestObject(unittest.TestCase):
self.assert_(GameScore.Query.where(score=previous_score + 1).exists(), self.assert_(GameScore.Query.where(score=previous_score + 1).exists(),
'Failed to increment score on backend') 'Failed to increment score on backend')
def testAssociatedObject(self):
"""test saving and associating a different object"""
collectedItem = CollectedItem(type="Sword", isAwesome=True)
collectedItem.save()
self.score.item = collectedItem
self.score.save()
# get the object, see if it has saved
qs = GameScore.Query.where(objectId=self.score.objectId).get()
self.assert_(isinstance(qs.item, Object),
"Associated CollectedItem is not of correct class")
self.assert_(qs.item.type == "Sword",
"Associated CollectedItem does not have correct attributes")
class TestQuery(unittest.TestCase): class TestQuery(unittest.TestCase):
"""Tests of an object's Queryset""" """Tests of an object's Queryset"""
...@@ -251,9 +268,9 @@ class TestUser(unittest.TestCase): ...@@ -251,9 +268,9 @@ class TestUser(unittest.TestCase):
def _get_user(self): def _get_user(self):
try: try:
user = User.signup(self.username, self.password) user = parse_rest.User.signup(self.username, self.password)
except: except:
user = User.Query.get(username=self.username) user = parse_rest.User.Query.get(username=self.username)
return user return user
def _destroy_user(self): def _destroy_user(self):
...@@ -261,8 +278,8 @@ class TestUser(unittest.TestCase): ...@@ -261,8 +278,8 @@ class TestUser(unittest.TestCase):
user and user.delete() user and user.delete()
def _get_logged_user(self): def _get_logged_user(self):
if User.Query.where(username=self.username).exists(): if parse_rest.User.Query.where(username=self.username).exists():
return User.login(self.username, self.password) return parse_rest.User.login(self.username, self.password)
else: else:
return self._get_user() return self._get_user()
...@@ -271,7 +288,7 @@ class TestUser(unittest.TestCase): ...@@ -271,7 +288,7 @@ class TestUser(unittest.TestCase):
self.password = TestUser.PASSWORD self.password = TestUser.PASSWORD
try: try:
u = User.login(self.USERNAME, self.PASSWORD) u = parse_rest.User.login(self.USERNAME, self.PASSWORD)
except parse_rest.ResourceRequestNotFound as e: except parse_rest.ResourceRequestNotFound as e:
# if the user doesn't exist, that's fine # if the user doesn't exist, that's fine
return return
...@@ -282,12 +299,12 @@ class TestUser(unittest.TestCase): ...@@ -282,12 +299,12 @@ class TestUser(unittest.TestCase):
def testCanSignUp(self): def testCanSignUp(self):
self._destroy_user() self._destroy_user()
user = User.signup(self.username, self.password) user = parse_rest.User.signup(self.username, self.password)
self.assert_(user is not None) self.assert_(user is not None)
def testCanLogin(self): def testCanLogin(self):
self._get_user() # User should be created here. self._get_user() # User should be created here.
user = User.login(self.username, self.password) user = parse_rest.User.login(self.username, self.password)
self.assert_(user.is_authenticated(), 'Login failed') self.assert_(user.is_authenticated(), 'Login failed')
def testCanUpdate(self): def testCanUpdate(self):
...@@ -298,13 +315,14 @@ class TestUser(unittest.TestCase): ...@@ -298,13 +315,14 @@ class TestUser(unittest.TestCase):
user.phone = phone_number user.phone = phone_number
user.save() user.save()
self.assert_(User.Query.where(phone=phone_number).exists(), self.assert_(parse_rest.User.Query.where(phone=phone_number).exists(),
'Failed to update user data. New info not on Parse') 'Failed to update user data. New info not on Parse')
def testCanQueryBySession(self): def testCanQueryBySession(self):
User.signup(self.username, self.password) parse_rest.User.signup(self.username, self.password)
logged = User.login(self.username, self.password) logged = parse_rest.User.login(self.username, self.password)
queried = User.Query.where(sessionToken=logged.sessionToken).get() queried = parse_rest.User.Query.where(sessionToken=logged.sessionToken
).get()
self.assert_(queried.objectId == logged.objectId, self.assert_(queried.objectId == logged.objectId,
'Could not find user %s by session' % logged.username) 'Could not find user %s by session' % logged.username)
......
...@@ -22,7 +22,7 @@ class TestCommand(Command): ...@@ -22,7 +22,7 @@ class TestCommand(Command):
setup( setup(
name='parse_rest', name='parse_rest',
version='0.9.2013', version='0.10.2013',
description='A client library for Parse.com\'.s REST API', description='A client library for Parse.com\'.s REST API',
url='https://github.com/dgrtwo/ParsePy', url='https://github.com/dgrtwo/ParsePy',
packages=['parse_rest'], packages=['parse_rest'],
......
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