milesrichardson / ParsePy

A relatively up-to-date fork of ParsePy, the Python wrapper for the Parse.com API. Originally maintained by @dgrtwo
MIT License
515 stars 184 forks source link

work with self-hosted parse server #138

Closed milesrichardson closed 8 years ago

milesrichardson commented 8 years ago

Major fixes:

  1. (improvement) Work with self-hosted parse-server
  2. (improvement) Work with self-hosted parse-server and batch_save
  3. (bugfix) batch_save calls response callback on ALL responses, even if some contain errors, and raises ParseBatchError([error1, error2, error3]) with list of all errors encountered during batch operation

The similar pull request from @basilfamer (#136) does NOT work with self-hosted parse-server, because it changes the value of API_ROOT after other classes have already set their class properties to the old API_ROOT, and because it does not support batch_saving (which depends on parse.com in the URL).

My pull request uses an environment variable. If you want to use self hosted parse server, it's this simple:

    import os
    os.environ["PARSE_API_ROOT"] = "http://your_server.com:1337/parse"

    # Everything else same as usual

    from parse_rest.datatypes import Function, Object, GeoPoint
    from parse_rest.connection import register
    from parse_rest.query import QueryResourceDoesNotExist
    from parse_rest.connection import ParseBatcher
    from parse_rest.core import ResourceRequestBadRequest, ParseError

    APPLICATION_ID = '...'
    REST_API_KEY = '...'
    MASTER_KEY = '...'

    register(APPLICATION_ID, REST_API_KEY, master_key=MASTER_KEY)

(If you want to use regular parse.com, just don't set the environment variable, and it will default to parse.com.)

Batch saving/deleting is also supported on self-hosted parse-server.

I also fixed a bug in batching. Previously, when performing batch_save on multiple objects, if just ONE of them failed, then the "response callback" for all objects after it would never execute. The result of this is that if you batch_save 10 objects, and the first 1 of them failed, then the "callback" would not execute on the remaining 9 objects. So for example if you were relying on that callback to set the objectId of saved objects, then NONE of the objects in the list would have their objectId set, even though they saved correctly.

Now, any errors encountered in a batch operation are appended to a list, and when the batch operation completes, if there were any errors it raises a ParseBatchError exception (child of ParseError) with .message set to the list of errors. For example:

# Batch save a list of two objects:
#   dupe_object is a duplicate violating a unique key constraint
#   dupe_object2 is a duplicate violating a unique key constraint
#   new_object is a new object satisfying the unique key constraint
#
# dupe_object and dupe_object2 will fail to save, and new_object will save successfully

dupe_object = list(MyClass.Query.all().limit(2))[0]
dupe_object2 = list(MyClass.Query.all().limit(2))[1]
new_object = MyClass(some_column=11111)
objects = [dupe_object + new_object]

batcher = ParseBatcher()
batcher.batch_save(objects)

Will raise exception (note the LIST of two errors)


Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/miles/ParsePy/parse_rest/connection.py", line 199, in batch_save
    self.batch(o.save for o in objects)
  File "/Users/miles/ParsePy/parse_rest/connection.py", line 195, in batch
    raise core.ParseBatchError(batched_errors)

ParseBatchError: [{u'code': 11000, u'error': u'E11000 duplicate key error index: myapp.MyClass.$my_column_1 dup key: { : 555555 }'}, {u'code': 11000, u'error': u'E11000 duplicate key error index: myapp.MyClass.$my_column_1 dup key: { : 44444 }'}]

And CRUCIALLY, the objectId field of the NON-duplicate object will be correctly set:

>>> #batch_save as above...
>>> print objects
[<MyClass:gOHuhPbGZJ>, <MyClass:None>, <MyClass:None>]

Whereas prior to my pull request, NONE of the objects would have objectId after saving, even though one of them saved correctly:

>>> #batch_save as above...
>>> print objects
[<MyClass:None>, <MyClass:None>, <MyClass:None>]
milesrichardson commented 8 years ago

FYI, tagging everyone who seems to be maintaining a fork of this project that updated it in the last month (since this is clearly no longer maintained)

@lebouquetin

@soby

@basilfamer

@lullis

@amitmalhotra

@deeGraYve

soby commented 8 years ago

I will thankfully be retiring my fork when Parse kills off their service.

flooie commented 8 years ago

@milesrichardson how would one go about using your branch? I'm attempting to test parsepy with my parse server

milesrichardson commented 8 years ago

@flooie first remove any existing parsepy install:

pip uninstall parse-rest

Then install my branch directly from my github repo:

pip install git+https://github.com/milesrichardson/ParsePy

danrobinson commented 8 years ago

Thanks very much! Appreciate the fix; we're working on bringing this up to date.