Skip to content

Commit 6a12c32

Browse files
committed
add pagination support
1 parent c2bb77c commit 6a12c32

File tree

6 files changed

+115
-39
lines changed

6 files changed

+115
-39
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ Install requirements and package:
3232

3333
Test:
3434

35-
nosetests -v --with-coverage --cover-package=microngo test.py
35+
nosetests -v --with-coverage --cover-erase --cover-package=microngo test.py

examples/basic.py

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from microngo import Microngo
22
from datetime import datetime
33
from random import randrange
4+
from time import time
45

56
#
67
# Client
@@ -75,4 +76,11 @@
7576
calls_data = db.query("Calls").find().limit(5).sort('No', 1).all()
7677
for call in calls_data:
7778
print("Call No", call.No)
78-
call.remove()
79+
call.remove()
80+
81+
#
82+
# Paginate
83+
#
84+
s = time()
85+
paginated_data = db.query("Calls").find().paginate(1, per_page=5)
86+
print("Time:", time()-s)

microngo/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
from .microngo import Microngo, Document, Query
1+
from microngo.microngo import Microngo, Document, Query
2+
from microngo.pagination import Pagination

microngo/microngo.py

+49-31
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from pymongo import MongoClient
22
from pymongo.cursor import Cursor
33
from pymongo.command_cursor import CommandCursor
4+
from microngo.pagination import Pagination
45

5-
class Microngo():
6+
class Microngo(object):
67
def __init__(self, *args, **kwargs):
78
'''
89
:param str db: Database name. This param is optional, you can use :func:`~microngo.Microngo.database` to set current database.
@@ -61,7 +62,7 @@ def insert(self, collection):
6162

6263
return Document(self._collection(collection))
6364

64-
class Document():
65+
class Document(object):
6566
def __init__(self, collection, data=None):
6667
'''
6768
:param obj collection: PyMongo collection object
@@ -70,16 +71,16 @@ def __init__(self, collection, data=None):
7071
'''
7172

7273
# Variables
73-
self.microngo_collection = collection
74-
self.microngo_payloads = []
75-
self.microngo_document_id = None
74+
self._microngo_collection = collection
75+
self._microngo_payloads = []
76+
self._microngo_document_id = None
7677

7778
# Dict data
7879
if data:
7980
if isinstance(data, dict):
8081
# Set documen id if exist
8182
if data.get("_id"):
82-
self.microngo_document_id = data.get("_id")
83+
self._microngo_document_id = data.get("_id")
8384
# Update data
8485
self.__dict__.update(data)
8586

@@ -91,38 +92,38 @@ def _get_payloads(self):
9192
payloads = dict(self.__dict__)
9293

9394
# Remove internal variables and return data
94-
del payloads['microngo_collection']
95-
del payloads['microngo_payloads']
96-
del payloads['microngo_document_id']
95+
del payloads['_microngo_collection']
96+
del payloads['_microngo_payloads']
97+
del payloads['_microngo_document_id']
9798
return payloads
9899

99100
def _clear_payloads(self):
100101
# Cache variable
101-
cache_collection = self.microngo_collection
102-
cache_payloads = self.microngo_payloads
103-
cache_document_id = self.microngo_document_id
102+
cache_collection = self._microngo_collection
103+
cache_payloads = self._microngo_payloads
104+
cache_document_id = self._microngo_document_id
104105

105106
# Clear self variables
106107
payloads = self.__dict__
107108
payloads.clear()
108109

109110
# Reasign internal variables from cache
110-
payloads['microngo_collection'] = cache_collection
111-
payloads['microngo_payloads'] = cache_payloads
112-
payloads['microngo_document_id'] = cache_document_id
111+
payloads['_microngo_collection'] = cache_collection
112+
payloads['_microngo_payloads'] = cache_payloads
113+
payloads['_microngo_document_id'] = cache_document_id
113114
return self
114115

115116
def add(self):
116117
'''
117118
Add document to list, then insert to collection with insert_many function.
118119
'''
119120

120-
if not self.microngo_document_id:
121+
if not self._microngo_document_id:
121122
# Get payloads
122123
payloads = self._get_payloads()
123124

124125
# Append to cache
125-
self.microngo_payloads.append(payloads)
126+
self._microngo_payloads.append(payloads)
126127

127128
# Clear previous payload and return
128129
self._clear_payloads()
@@ -141,38 +142,38 @@ def save(self):
141142
'''
142143
Save document to collection.
143144
144-
:return: str or ObjectID
145+
:return: list of OjectId or single ObjectId
145146
'''
146147

147-
if self.microngo_document_id:
148+
if self._microngo_document_id:
148149
# Update
149-
self.microngo_collection.update_one(
150-
{'_id': self.microngo_document_id},
150+
self._microngo_collection.update_one(
151+
{'_id': self._microngo_document_id},
151152
{'$set': self._get_payloads()},
152153
upsert=False
153154
)
154-
return self.microngo_document_id
155+
return self._microngo_document_id
155156
else:
156157
# Insert
157-
if len(self.microngo_payloads) > 0:
158-
data = self.microngo_payloads
159-
return self.microngo_collection.insert_many(data).inserted_ids
158+
if len(self._microngo_payloads) > 0:
159+
data = self._microngo_payloads
160+
return self._microngo_collection.insert_many(data).inserted_ids
160161
else:
161162
data = self._get_payloads()
162-
doc_id = self.microngo_collection.insert_one(data).inserted_id
163-
self.microngo_document_id = doc_id
163+
doc_id = self._microngo_collection.insert_one(data).inserted_id
164+
self._microngo_document_id = doc_id
164165
return doc_id
165166

166167
def remove(self):
167168
'''
168169
Remove document from collection.
169170
'''
170171

171-
if self.microngo_document_id:
172+
if self._microngo_document_id:
172173
# Delete
173-
self.microngo_collection.delete_one({'_id': self.microngo_document_id})
174+
self._microngo_collection.delete_one({'_id': self._microngo_document_id})
174175

175-
class Query():
176+
class Query(object):
176177
def __init__(self, collection, cursor=None):
177178
'''
178179
:param obj collection: PyMongo collection object
@@ -281,4 +282,21 @@ def all(self):
281282
else:
282283
raise Exception("cursor type is '%s'" % (type(self.cursor)))
283284

284-
return documents
285+
return documents
286+
287+
def paginate(self, page, per_page=20):
288+
'''
289+
Crate pagination query
290+
291+
:return: :class:`~pagination.Pagination` or None
292+
'''
293+
if page < 1:
294+
return None
295+
296+
total = self.cursor.count()
297+
items = self.skip((page - 1) * per_page).limit(per_page).all()
298+
299+
if len(items) < 1 and page != 1:
300+
return None
301+
302+
return Pagination(self, page, per_page, total, items)

microngo/pagination.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from math import ceil
2+
3+
class Pagination(object):
4+
def __init__(self, query, page, per_page, total, items):
5+
self.query = query
6+
self.page = page
7+
self.per_page = per_page
8+
self.total = total
9+
self.items = items
10+
11+
@property
12+
def pages(self):
13+
'''The total number of pages'''
14+
return int(ceil(self.total / float(self.per_page)))
15+
16+
@property
17+
def next_num(self):
18+
'''The next page number.'''
19+
return self.page + 1
20+
21+
def has_next(self):
22+
'''Returns ``True`` if a next page exists.'''
23+
return self.page < self.pages
24+
25+
@property
26+
def prev_num(self):
27+
'''The previous page number.'''
28+
return self.page - 1
29+
30+
def has_prev(self):
31+
'''Returns ``True`` if a previous page exists.'''
32+
return self.page > 1

test.py

+22-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from microngo import Microngo, Query, Document
1+
from microngo import Microngo, Query, Document, Pagination
22
from datetime import datetime
33
from bson.objectid import ObjectId
44
from random import randrange
@@ -88,7 +88,24 @@ def test_05_find_all(self):
8888
for i in documents:
8989
self.assertEqual(type(i.No), int)
9090

91-
def test_06_raw(self):
91+
def test_06_pagination(self):
92+
# Normal state
93+
documents = self.db.query("collection_two").find().paginate(1)
94+
self.assertEqual(type(documents), Pagination)
95+
# If page below 1
96+
documents_2 = self.db.query("collection_two").find().paginate(0, per_page=20)
97+
self.assertEqual(documents_2, None)
98+
# If result empty and page higher than 1
99+
documents_3 = self.db.query("collection_two").find({"No": 100}).paginate(2)
100+
self.assertEqual(documents_3, None)
101+
# Get info from pagination
102+
self.assertEqual(type(documents.pages), int)
103+
self.assertEqual(type(documents.next_num), int)
104+
self.assertEqual(type(documents.has_next()), bool)
105+
self.assertEqual(type(documents.prev_num), int)
106+
self.assertEqual(type(documents.has_prev()), bool)
107+
108+
def test_07_raw(self):
92109
document = self.db.query("collection_two").find_by()
93110
raw_query = document.raw()
94111
docs = document.first()
@@ -97,7 +114,7 @@ def test_06_raw(self):
97114
self.assertEqual(type(docs), Document)
98115
self.assertEqual(type(raw_docs), dict)
99116

100-
def test_07_update(self):
117+
def test_08_update(self):
101118
documents = self.db.query("collection_two").find().all()
102119
self.assertEqual(type(documents), list)
103120
# Check per item
@@ -107,7 +124,7 @@ def test_07_update(self):
107124
result = i.save()
108125
self.assertEqual(type(result), ObjectId)
109126

110-
def test_08_error_test(self):
127+
def test_09_error_test(self):
111128
# Test db error
112129
with self.assertRaises(Exception) as ctx:
113130
self.db2.query("collection_two").find().all()
@@ -138,7 +155,7 @@ def test_08_error_test(self):
138155
with self.assertRaises(Exception) as ctx:
139156
doc_all_b = self.db.query("collection_one").find_one().all()
140157

141-
def test_09_delete(self):
158+
def test_10_delete(self):
142159
# Delete one
143160
document = self.db.query("collection_one").find_one().one()
144161
self.assertEqual(type(document), Document)

0 commit comments

Comments
 (0)