Commit 84ddd38e by Roman Krejcik

independent derived queries - filter, limit, skip, select_related methods…

independent derived queries - filter, limit, skip, select_related methods returns queryset copy instead of self

This is proper implementation of idea suggested by orignal limit and skip methods based on metaclass (but not working at all)
It works same as Django queries - original unfiltered query can be still executed or used to create
different filter.
parent 8aaea7fd
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import json import json
import copy
import collections import collections
...@@ -78,6 +79,13 @@ class Queryset(object): ...@@ -78,6 +79,13 @@ class Queryset(object):
self._options = {} self._options = {}
self._result_cache = None self._result_cache = None
def __deepcopy__(self, memo):
q = self.__class__(self._manager)
q._where = copy.deepcopy(self._where, memo)
q._options = copy.deepcopy(self._options, memo)
q._select_related.extend(self._select_related)
return q
def __iter__(self): def __iter__(self):
return iter(self._fetch()) return iter(self._fetch())
...@@ -111,37 +119,40 @@ class Queryset(object): ...@@ -111,37 +119,40 @@ class Queryset(object):
return self._result_cache return self._result_cache
def filter(self, **kw): def filter(self, **kw):
q = copy.deepcopy(self)
for name, value in kw.items(): for name, value in kw.items():
parse_value = Queryset.convert_to_parse(value) parse_value = Queryset.convert_to_parse(value)
attr, operator = Queryset.extract_filter_operator(name) attr, operator = Queryset.extract_filter_operator(name)
if operator is None: if operator is None:
self._where[attr] = parse_value q._where[attr] = parse_value
elif operator == 'relatedTo': elif operator == 'relatedTo':
self._where['$' + operator] = parse_value q._where['$' + operator] = parse_value
else: else:
try: if not isinstance(q._where[attr], dict):
self._where[attr]['$' + operator] = parse_value q._where[attr] = {}
except TypeError: q._where[attr]['$' + operator] = parse_value
# self._where[attr] wasn't settable return q
raise ValueError("Cannot filter for a constraint after filtering for a specific value")
return self
def limit(self, value): def limit(self, value):
self._options['limit'] = int(value) q = copy.deepcopy(self)
return self q._options['limit'] = int(value)
return q
def skip(self, value): def skip(self, value):
self._options['skip'] = int(value) q = copy.deepcopy(self)
return self q._options['skip'] = int(value)
return q
def order_by(self, order, descending=False): def order_by(self, order, descending=False):
q = copy.deepcopy(self)
# add a minus sign before the order value if descending == True # add a minus sign before the order value if descending == True
self._options['order'] = descending and ('-' + order) or order q._options['order'] = descending and ('-' + order) or order
return self return q
def select_related(self, *fields): def select_related(self, *fields):
self._select_related.extend(fields) q = copy.deepcopy(self)
return self q._select_related.extend(fields)
return q
def count(self): def count(self):
return self._fetch(count=True) return self._fetch(count=True)
......
...@@ -288,13 +288,26 @@ class TestQuery(unittest.TestCase): ...@@ -288,13 +288,26 @@ class TestQuery(unittest.TestCase):
self.assertEqual(len(scores_ne_2), 4) self.assertEqual(len(scores_ne_2), 4)
self.assertTrue(all([s.score != 2 for s in scores_ne_2])) self.assertTrue(all([s.score != 2 for s in scores_ne_2]))
# test chaining def testChaining(self):
lt_4_gt_2 = GameScore.Query.filter(score__lt=4).filter(score__gt=2) lt_4_gt_2 = GameScore.Query.filter(score__lt=4).filter(score__gt=2)
self.assertEqual(len(lt_4_gt_2), 1, 'chained lt+gt not working') self.assertEqual(len(lt_4_gt_2), 1, 'chained lt+gt not working')
self.assertEqual(lt_4_gt_2[0].score, 3, 'chained lt+gt not working') self.assertEqual(lt_4_gt_2[0].score, 3, 'chained lt+gt not working')
q = GameScore.Query.filter(score__gt=3, score__lt=3) q = GameScore.Query.filter(score__gt=3, score__lt=3)
self.assertFalse(q.exists(), "chained lt+gt not working") self.assertFalse(q.exists(), "chained lt+gt not working")
# test original queries are idependent after filting
q_all = GameScore.Query.all()
q_special = q_all.filter(score__gt=3)
self.assertEqual(len(q_all), 5)
self.assertEqual(len(q_special), 2)
q_all = GameScore.Query.all()
q_limit = q_all.limit(1)
self.assertEqual(len(q_all), 5)
self.assertEqual(len(q_limit), 1)
def testOrderBy(self): def testOrderBy(self):
"""test three options- order, limit, and skip""" """test three options- order, limit, and skip"""
scores_ordered = GameScore.Query.all().order_by("score") scores_ordered = GameScore.Query.all().order_by("score")
......
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