Skip to content

Commit c375cff

Browse files
authored
Merge pull request #21 from novafloss/page-generator
Added a page generator
2 parents d488e22 + 4dc4d22 commit c375cff

File tree

4 files changed

+58
-16
lines changed

4 files changed

+58
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# master (unreleased)
44

55
- Deal with OneToOne Primary Keys (#19).
6+
- Expose the util function ``chunkator_page``.
67

78
## 1.1.0 (2017-03-25)
89

README.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,16 @@ You can also use ``values()``:
4444
you'll have several small queries. This will save your RAM instead, because
4545
you'll not load a huge queryset result before looping on it.
4646

47+
If you want to manipulate the pages directly, you can use `chunkator_page`:
48+
49+
.. code:: python
50+
51+
from chunkator import chunkator_page
52+
queryset = LargeModel.objects.all().values('pk')
53+
for page in chunkator_page(queryset, 200):
54+
launch_some_task([item['pk'] for item in page])
4755
4856
License
4957
=======
5058

51-
MIT License.
59+
MIT License.

chunkator/__init__.py

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,9 @@ class MissingPkFieldException(Exception):
1212
pass
1313

1414

15-
def chunkator(source_qs, chunk_size, query_log=None):
15+
def chunkator_page(source_qs, chunk_size, query_log=None):
1616
"""
17-
Yield over a queryset by chunks.
18-
19-
This method does not involve counting elements or measuring the iterable
20-
length. We're saving at least a ``count()`` query on QuerySets, or a
21-
CPU-and-RAM-consuming ``len(queryset)`` query.
17+
Yield pages of a queryset.
2218
"""
2319
pk = None
2420
# In django 1.9, _fields is always present and `None` if 'values()' is used
@@ -42,14 +38,35 @@ def chunkator(source_qs, chunk_size, query_log=None):
4238
page = queryset[:chunk_size]
4339
if query_log is not None:
4440
query_log.write('{page.query}\n'.format(page=page))
45-
nb_items = 0
46-
for item in page:
47-
# source_qs._fields exists *and* is not none when using "values()"
48-
if has_fields:
49-
pk = item["pk"]
50-
else:
51-
pk = item.pk
52-
nb_items += 1
53-
yield item
41+
page = list(page)
42+
nb_items = len(page)
43+
44+
if nb_items == 0:
45+
raise StopIteration
46+
47+
last_item = page[-1]
48+
# source_qs._fields exists *and* is not none when using "values()"
49+
if has_fields:
50+
pk = last_item["pk"]
51+
else:
52+
pk = last_item.pk
53+
54+
yield page
55+
5456
if nb_items < chunk_size:
5557
raise StopIteration
58+
59+
60+
def chunkator(source_qs, chunk_size, query_log=None):
61+
"""
62+
Yield over a queryset by chunks.
63+
64+
This method does not involve counting elements or measuring the iterable
65+
length. We're saving at least a ``count()`` query on QuerySets, or a
66+
CPU-and-RAM-consuming ``len(queryset)`` query.
67+
"""
68+
for page in chunkator_page(source_qs,
69+
chunk_size,
70+
query_log=query_log):
71+
for item in page:
72+
yield item

demo/demo_chunkator/tests.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from chunkator import MissingPkFieldException
66
from chunkator import chunkator
7+
from chunkator import chunkator_page
78
from demo_chunkator.models import Book
89
from demo_chunkator.models import Cover
910
from demo_chunkator.models import Profile
@@ -170,3 +171,18 @@ def test_query_log(self):
170171
# Should be 0 for the first query
171172
# Should occur once for other queries
172173
self.assertTrue(query.count('."uuid" >') <= 1, query)
174+
175+
176+
class ChunkatorPageTest(TestCase):
177+
def setUp(self):
178+
super(ChunkatorPageTest, self).setUp()
179+
for nb in range(3):
180+
Book.objects.create(
181+
title="Title #{}".format(nb),
182+
author="Author #{}".format(nb)
183+
)
184+
185+
def test_chunkator_page(self):
186+
qs = Book.objects.all().values('pk')
187+
pages = list(chunkator_page(qs, 2))
188+
self.assertEqual(pages, [[{'pk': 1}, {'pk': 2}], [{'pk': 3}]])

0 commit comments

Comments
 (0)