Commits

Anonymous committed 3747ada

added support for cursors

Comments (0)

Files changed (3)

             pks_only = True
         self.db_table = self.query.get_meta().db_table
         self.pks_only = pks_only
-        self.gae_query = [Query(self.db_table, keys_only=self.pks_only)]
+        start_cursor = getattr(self.query, '_gae_start_cursor', None)
+        end_cursor = getattr(self.query, '_gae_end_cursor', None)
+        self.gae_query = [Query(self.db_table, keys_only=self.pks_only,
+                                cursor=start_cursor, end_cursor=end_cursor)]
 
     # This is needed for debugging
     def __repr__(self):
     @safe_call
     def fetch(self, low_mark, high_mark):
         query = self._build_query()
+        executed = False
         if self.excluded_pks and high_mark is not None:
             high_mark += len(self.excluded_pks)
         if self.pk_filters is not None:
                 if low_mark:
                     kw['offset'] = low_mark
                 results = query.Run(**kw)
+                executed = True
             elif high_mark > low_mark:
                 results = query.Get(high_mark - low_mark, low_mark)
+                executed = True
             else:
                 results = ()
 
                 continue
             yield self._make_entity(entity)
 
+        if executed and not isinstance(query, MultiQuery):
+            self.query._gae_cursor = query.GetCompiledCursor()
+
     @safe_call
     def count(self, limit=None):
         if self.pk_filters is not None:
             return len(self.get_matching_pk(0, limit))
         if self.excluded_pks:
-            return len(list(self.fetch(0, 300)))
-        return self._build_query().Count(limit)
+            return len(list(self.fetch(0, 2000)))
+        kw = {}
+        if limit is not None:
+            kw['limit'] = limit
+        return self._build_query().Count(**kw)
 
     @safe_call
     def delete(self):
+import base64
+from google.appengine.datastore.datastore_pb import CompiledCursor
+
+def get_cursor(queryset):
+    # Evaluate QuerySet
+    len(queryset)
+    cursor = getattr(queryset.query, '_gae_cursor', None)
+    return base64.urlsafe_b64encode(cursor.Encode())
+
+def set_cursor(queryset, start=None, end=None):
+    if start is not None:
+        start = base64.urlsafe_b64decode(str(start))
+        start = CompiledCursor(start)
+        queryset.query._gae_start_cursor = start
+    if end is not None:
+        end = base64.urlsafe_b64decode(str(end))
+        end = CompiledCursor(end)
+        queryset.query._gae_end_cursor = end
+    # Evaluate QuerySet
+    len(queryset)
 from .testmodels import FieldsWithOptionsModel, EmailModel, DateTimeModel, OrderedModel
+from ..db.utils import get_cursor
 import datetime, time
 from django.test import TestCase
 from django.db.models import Q
 from django.db.utils import DatabaseError
+from djangoappengine.db.utils import set_cursor
 
 class FilterTest(TestCase):
     floats = [5.3, 2.6, 9.1, 1.58]
                             'email')[::2]],
                           ['app-engine@scholardocs.com', 'rinnengan@sage.de',])
 
+    def test_cursor(self):
+        results = list(FieldsWithOptionsModel.objects.all())
+        cursor = None
+        for item in results:
+            query = FieldsWithOptionsModel.objects.all()[:1]
+            if cursor is not None:
+                set_cursor(query, cursor)
+            next = query[0]
+            self.assertEqual(next.pk, item.pk)
+            cursor = get_cursor(query)
+        query = FieldsWithOptionsModel.objects.all()[:1]
+        set_cursor(query, cursor)
+        self.assertEqual(list(query), [])
+
     def test_Q_objects(self):
         self.assertEquals([entity.email for entity in
                           FieldsWithOptionsModel.objects.filter(