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)"
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
--------
......
......@@ -107,7 +107,7 @@ class Date(ParseType):
class Binary(ParseType):
@classmethod
def from_native(self, **kw):
def from_native(cls, **kw):
return cls(kw.get('base64', ''))
def __init__(self, encoded_string):
......@@ -121,7 +121,7 @@ class Binary(ParseType):
class GeoPoint(ParseType):
@classmethod
def from_native(self, **kw):
def from_native(cls, **kw):
return cls(kw.get('latitude'), kw.get('longitude'))
def __init__(self, latitude, longitude):
......@@ -139,7 +139,7 @@ class GeoPoint(ParseType):
class File(ParseType):
@classmethod
def from_native(self, **kw):
def from_native(cls, **kw):
return cls(kw.get('url'), kw.get('name'))
def __init__(self, url, name):
......@@ -211,7 +211,7 @@ class Function(ParseBase):
return self.POST("/" + self.name, **kwargs)
class ParseResource(ParseBase):
class ParseResource(ParseBase, Pointer):
PROTECTED_ATTRIBUTES = ['objectId', 'createdAt', 'updatedAt']
......@@ -221,9 +221,9 @@ class ParseResource(ParseBase):
def __init__(self, **kw):
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
protected_attributes = self.__class__.PROTECTED_ATTRIBUTES
......@@ -265,7 +265,7 @@ class ParseResource(ParseBase):
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.objectId = response_dict['objectId']
......@@ -274,7 +274,7 @@ class ParseResource(ParseBase):
# URL: /1/classes/<className>/<objectId>
# 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']
def delete(self):
......@@ -306,7 +306,8 @@ class Object(ParseResource):
def factory(cls, class_name):
class DerivedClass(cls):
pass
DerivedClass.__name__ = class_name
DerivedClass.__name__ = str(class_name)
DerivedClass.set_endpoint_root()
return DerivedClass
@classmethod
......@@ -366,7 +367,7 @@ class User(ParseResource):
return self.__class__.PUT(
self._absolute_url,
extra_headers=session_header,
**self._to_native())
**self._to_dict())
@login_required
def delete(self):
......
......@@ -13,7 +13,6 @@ import datetime
import __init__ as parse_rest
from __init__ import GeoPoint, Object
from user import User
import query
......@@ -57,6 +56,10 @@ class Review(Object):
pass
class CollectedItem(Object):
pass
class TestObject(unittest.TestCase):
def setUp(self):
self.score = GameScore(
......@@ -116,6 +119,20 @@ class TestObject(unittest.TestCase):
self.assert_(GameScore.Query.where(score=previous_score + 1).exists(),
'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):
"""Tests of an object's Queryset"""
......@@ -251,9 +268,9 @@ class TestUser(unittest.TestCase):
def _get_user(self):
try:
user = User.signup(self.username, self.password)
user = parse_rest.User.signup(self.username, self.password)
except:
user = User.Query.get(username=self.username)
user = parse_rest.User.Query.get(username=self.username)
return user
def _destroy_user(self):
......@@ -261,8 +278,8 @@ class TestUser(unittest.TestCase):
user and user.delete()
def _get_logged_user(self):
if User.Query.where(username=self.username).exists():
return User.login(self.username, self.password)
if parse_rest.User.Query.where(username=self.username).exists():
return parse_rest.User.login(self.username, self.password)
else:
return self._get_user()
......@@ -271,7 +288,7 @@ class TestUser(unittest.TestCase):
self.password = TestUser.PASSWORD
try:
u = User.login(self.USERNAME, self.PASSWORD)
u = parse_rest.User.login(self.USERNAME, self.PASSWORD)
except parse_rest.ResourceRequestNotFound as e:
# if the user doesn't exist, that's fine
return
......@@ -282,12 +299,12 @@ class TestUser(unittest.TestCase):
def testCanSignUp(self):
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)
def testCanLogin(self):
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')
def testCanUpdate(self):
......@@ -298,13 +315,14 @@ class TestUser(unittest.TestCase):
user.phone = phone_number
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')
def testCanQueryBySession(self):
User.signup(self.username, self.password)
logged = User.login(self.username, self.password)
queried = User.Query.where(sessionToken=logged.sessionToken).get()
parse_rest.User.signup(self.username, self.password)
logged = parse_rest.User.login(self.username, self.password)
queried = parse_rest.User.Query.where(sessionToken=logged.sessionToken
).get()
self.assert_(queried.objectId == logged.objectId,
'Could not find user %s by session' % logged.username)
......
......@@ -22,7 +22,7 @@ class TestCommand(Command):
setup(
name='parse_rest',
version='0.9.2013',
version='0.10.2013',
description='A client library for Parse.com\'.s REST API',
url='https://github.com/dgrtwo/ParsePy',
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