Commit 648db13d by Roman Krejcik

selected_related - Django like method to declare related objects fetched with…

selected_related - Django like method to declare related objects fetched with based query (using 'include' parameter from Parse API)

https://www.parse.com/docs/rest#queries-relational
parent ebcfa9c0
......@@ -311,6 +311,14 @@ page_one = posts.limit(10) # Will return the most 10 recent posts.
page_two = posts.skip(10).limit(10) # Will return posts 11-20
~~~~~
#### Related objects
You can specify "join" attributes to get related object with single query.
~~~~~ {python}
posts = Post.Query.all().select_related("author", "editor")
~~~~~
#### Composability/Chaining of Querysets
The example above can show the most powerful aspect of Querysets, that
......
......@@ -27,7 +27,6 @@ class ParseType(object):
# if its not a parse type -- simply return it. This means it wasn't a "special class"
if not is_parse_type:
return parse_data
# determine just which kind of parse type this element is - ie: a built in parse type such as File, Pointer, User etc
......@@ -35,12 +34,12 @@ class ParseType(object):
# if its a pointer, we need to handle to ensure that we don't mishandle a circular reference
if parse_type == "Pointer":
# grab the pointer object here
return Pointer.from_native(class_name, **parse_data)
# return a recursive handled Pointer method
return True
# embedded object by select_related
if parse_type == "Object":
return EmbeddedObject.from_native(class_name, **parse_data)
# now handle the other parse types accordingly
native = {
......@@ -90,22 +89,26 @@ class ParseType(object):
class Pointer(ParseType):
@classmethod
def from_native(cls, parent_class_name = None, **kw):
def _prevent_circular(cls, parent_class_name, objectData):
# TODO this should be replaced with more clever checking, instead of simple class mathching original id should be compared
# also circular refs through more object are now ignored, in fact lazy loaded references will be best solution
objectData = dict(objectData)
# now lets see if we have any references to the parent class here
for key, value in objectData.iteritems():
if isinstance(value, dict) and "className" in value and value["className"] == parent_class_name:
# simply put the reference here as a string -- not sure what the drawbacks are for this but it works for me
objectData[key] = value["objectId"]
return objectData
@classmethod
def from_native(cls, parent_class_name=None, **kw):
# grab the object data manually here so we can manipulate it before passing back an actual object
klass = Object.factory(kw.get('className'))
objectData = klass.GET("/" + kw.get('objectId'))
# now lets check if we have circular references here
if parent_class_name:
# now lets see if we have any references to the parent class here
for key, value in objectData.iteritems():
if type(value) == dict and "className" in value and value["className"] == parent_class_name:
# simply put the reference here as a string -- not sure what the drawbacks are for this but it works for me
objectData[key] = value["objectId"]
objectData = cls._prevent_circular(parent_class_name, objectData)
# set a temporary flag that will remove the recursive pointer types etc
klass = Object.factory(kw.get('className'))
......@@ -123,6 +126,15 @@ class Pointer(ParseType):
}
class EmbeddedObject(ParseType):
@classmethod
def from_native(cls, parent_class_name=None, **kw):
if parent_class_name:
kw = Pointer._prevent_circular(parent_class_name, kw)
klass = Object.factory(kw.get('className'))
return klass(**kw)
class Relation(ParseType):
@classmethod
def from_native(cls, **kw):
......
......@@ -96,7 +96,9 @@ class Queryset(object):
def __init__(self, manager):
self._manager = manager
self._where = collections.defaultdict(dict)
self._select_related = []
self._options = {}
self._result_cache = None
def __iter__(self):
return iter(self._fetch())
......@@ -104,7 +106,14 @@ class Queryset(object):
def __len__(self):
return self._fetch(count=True)
def __getitem__(self, key):
if isinstance(key, slice):
raise AttributeError("Slice is not supported for now.")
return self._fetch()[key]
def _fetch(self, count=False):
if self._result_cache:
return len(self._result_cache) if count else self._result_cache
"""
Return a list of objects matching query, or if count == True return
only the number of objects matching.
......@@ -112,12 +121,14 @@ class Queryset(object):
options = dict(self._options) # make a local copy
if self._where:
# JSON encode WHERE values
where = json.dumps(self._where)
options.update({'where': where})
options['where'] = json.dumps(self._where)
if self._select_related:
options['include'] = ','.join(self._select_related)
if count:
return self._manager._count(**options)
return self._manager._fetch(**options)
self._result_cache = self._manager._fetch(**options)
return self._result_cache
def filter(self, **kw):
for name, value in kw.items():
......@@ -141,6 +152,10 @@ class Queryset(object):
self._options['order'] = descending and ('-' + order) or order
return self
def select_related(self, *fields):
self._select_related.extend(fields)
return self
def count(self):
return len(self)
......
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