Skip to content

Commit 79c73c0

Browse files
authored
Merge pull request #5 from sinisaos/add_get_readable_method
added get_readable method
2 parents 0db0338 + dfdb806 commit 79c73c0

File tree

4 files changed

+208
-83
lines changed

4 files changed

+208
-83
lines changed

example/home/tables.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
from piccolo.apps.user.tables import BaseUser
2+
from piccolo.columns import Boolean, ForeignKey, Timestamp, Varchar
3+
from piccolo.columns.readable import Readable
14
from piccolo.table import Table
2-
from piccolo.columns import Varchar, Boolean
35

46

57
class Task(Table):
@@ -9,3 +11,9 @@ class Task(Table):
911

1012
name = Varchar()
1113
completed = Boolean(default=False)
14+
created_at = Timestamp()
15+
task_user = ForeignKey(references=BaseUser)
16+
17+
@classmethod
18+
def get_readable(cls):
19+
return Readable(template="%s", columns=[cls.task_user.username])

piccolo_cursor_pagination/pagination.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ class CursorPagination:
2727
async def get_cursor_rows(self, table: t.Type[Table], request: Request):
2828
# headers to adding next_cursor
2929
headers: t.Dict[str, str] = {}
30+
all_columns: t.Any = table.all_columns()
3031

3132
if self.order_by == "id":
3233
# query where limit is equal to page_size plus
3334
# one in ASC order
34-
query = table.select().order_by(
35+
query = table.select(all_columns, table.get_readable()).order_by(
3536
table._meta.primary_key, ascending=True
3637
)
3738
query = query.limit(self.page_size + 1)
@@ -73,25 +74,12 @@ async def get_cursor_rows(self, table: t.Type[Table], request: Request):
7374
# set new value to next_cursor
7475
next_cursor = self.encode_cursor(str(rows[-1]["id"]))
7576
headers["cursor"] = next_cursor
76-
# if the last_row of whole data set is in rows, it means that
77-
# there are no more results, then we must use the first item
78-
# in the rows to get the correct result of the previous rows
79-
last_row = await (
80-
table.select()
81-
.limit(1)
82-
.order_by(table._meta.primary_key, ascending=False)
83-
.first()
84-
.run()
85-
)
86-
if (
87-
await self.decode_cursor(next_cursor, table)
88-
== last_row["id"]
89-
):
77+
if len(rows) <= self.page_size:
9078
headers["cursor"] = self.encode_cursor(str(rows[0]["id"]))
9179
else:
9280
# query where limit is equal to page_size plus
9381
# one in DESC order
94-
query = table.select().order_by(
82+
query = table.select(all_columns, table.get_readable()).order_by(
9583
table._meta.primary_key, ascending=False
9684
)
9785
query = query.limit(self.page_size + 1)
@@ -139,14 +127,7 @@ async def get_cursor_rows(self, table: t.Type[Table], request: Request):
139127
# set new value to next_cursor
140128
next_cursor = self.encode_cursor(str(rows[-1]["id"]))
141129
headers["cursor"] = next_cursor
142-
# if the last_row of whole data set is in rows, it means that
143-
# there are no more results, then we must use the first item
144-
# in the rows to get the correct result of the previous rows
145-
last_row = await (table.select().limit(1).first().run())
146-
if (
147-
await self.decode_cursor(next_cursor, table)
148-
== last_row["id"]
149-
):
130+
if len(rows) <= self.page_size:
150131
headers["cursor"] = self.encode_cursor(str(rows[0]["id"]))
151132

152133
query = query.limit(self.page_size)

tests/test_cursor_pagination_asc.py

Lines changed: 99 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from unittest import TestCase
33

44
from fastapi import FastAPI, Request
5-
from piccolo.columns import Integer, Varchar
5+
from piccolo.columns import ForeignKey, Integer, Varchar
66
from piccolo.columns.readable import Readable
77
from piccolo.table import Table
88
from starlette.responses import JSONResponse
@@ -11,13 +11,18 @@
1111
from piccolo_cursor_pagination.pagination import CursorPagination
1212

1313

14+
class Director(Table):
15+
name = Varchar(length=100, required=True)
16+
17+
1418
class Movie(Table):
1519
name = Varchar(length=100, required=True)
1620
rating = Integer()
21+
director = ForeignKey(references=Director)
1722

1823
@classmethod
1924
def get_readable(cls):
20-
return Readable(template="%s", columns=[cls.name])
25+
return Readable(template="%s", columns=[cls.director.name])
2126

2227

2328
app = FastAPI()
@@ -30,22 +35,26 @@ async def movies(
3035
__previous: t.Optional[str] = None,
3136
):
3237
try:
33-
cursor = request.query_params["__cursor"]
34-
paginator = CursorPagination(cursor=cursor, page_size=1, order_by="id")
35-
rows_result, headers_result = await paginator.get_cursor_rows(
36-
Movie, request
37-
)
38-
rows = await rows_result.run()
39-
headers = headers_result
40-
response = JSONResponse(
41-
{"rows": rows[::-1]},
42-
headers={
43-
"next_cursor": headers["cursor"],
44-
},
45-
)
38+
previous = request.query_params["__previous"]
39+
if previous:
40+
paginator = CursorPagination(
41+
cursor=__cursor, page_size=2, order_by="id"
42+
)
43+
rows_result, headers_result = await paginator.get_cursor_rows(
44+
Movie, request
45+
)
46+
rows = await rows_result.run()
47+
headers = headers_result
48+
response = JSONResponse(
49+
{"rows": rows[::-1]},
50+
headers={
51+
"next_cursor": headers["cursor"],
52+
},
53+
)
4654
except KeyError:
47-
cursor = request.query_params["__cursor"]
48-
paginator = CursorPagination(cursor=cursor, page_size=1, order_by="id")
55+
paginator = CursorPagination(
56+
cursor=__cursor, page_size=2, order_by="id"
57+
)
4958
rows_result, headers_result = await paginator.get_cursor_rows(
5059
Movie, request
5160
)
@@ -62,29 +71,67 @@ async def movies(
6271

6372
class TestCursorPaginationAsc(TestCase):
6473
def setUp(self):
74+
Director.create_table(if_not_exists=True).run_sync()
6575
Movie.create_table(if_not_exists=True).run_sync()
6676

6777
def tearDown(self):
78+
Director.alter().drop_table().run_sync()
6879
Movie.alter().drop_table().run_sync()
6980

7081
def test_cursor_pagination_asc(self):
7182
"""
7283
If cursor is applied
7384
"""
85+
Director.insert(
86+
Director(name="George Lucas"),
87+
Director(name="Ridley Scott"),
88+
).run_sync()
89+
7490
Movie.insert(
75-
Movie(name="Star Wars", rating=93),
76-
Movie(name="Lord of the Rings", rating=90),
91+
Movie(name="Star Wars", rating=93, director=1),
92+
Movie(name="Blade Runner", rating=90, director=2),
93+
Movie(name="Alien", rating=91, director=2),
7794
).run_sync()
7895

7996
client = TestClient(app)
8097
response = client.get("/movies/", params={"__cursor": ""})
8198
self.assertTrue(response.status_code, 200)
82-
self.assertEqual(response.headers["next_cursor"], "MQ==")
99+
self.assertEqual(response.headers["next_cursor"], "Mw==")
100+
self.assertEqual(
101+
response.json(),
102+
{
103+
"rows": [
104+
{
105+
"id": 1,
106+
"name": "Star Wars",
107+
"rating": 93,
108+
"director": 1,
109+
"readable": "George Lucas",
110+
},
111+
{
112+
"id": 2,
113+
"name": "Blade Runner",
114+
"rating": 90,
115+
"director": 2,
116+
"readable": "Ridley Scott",
117+
},
118+
]
119+
},
120+
)
121+
response = client.get("/movies/", params={"__cursor": "Mw=="})
122+
self.assertTrue(response.status_code, 200)
123+
self.assertEqual(response.headers["next_cursor"], "Mw==")
83124
self.assertEqual(
84125
response.json(),
85126
{
86127
"rows": [
87-
{"id": 1, "name": "Star Wars", "rating": 93},
128+
{
129+
"id": 3,
130+
"name": "Alien",
131+
"rating": 91,
132+
"director": 2,
133+
"readable": "Ridley Scott",
134+
},
88135
]
89136
},
90137
)
@@ -93,22 +140,41 @@ def test_cursor_pagination_asc_previous(self):
93140
"""
94141
If cursor ad previous is applied
95142
"""
143+
Director.insert(
144+
Director(name="George Lucas"),
145+
Director(name="Ridley Scott"),
146+
).run_sync()
147+
96148
Movie.insert(
97-
Movie(name="Star Wars", rating=93),
98-
Movie(name="Lord of the Rings", rating=90),
149+
Movie(name="Star Wars", rating=93, director=1),
150+
Movie(name="Blade Runner", rating=90, director=2),
151+
Movie(name="Alien", rating=91, director=2),
99152
).run_sync()
100153

101154
client = TestClient(app)
102155
response = client.get(
103-
"/movies/", params={"__cursor": "Mg==", "__previous": "yes"}
156+
"/movies/", params={"__cursor": "Mw==", "__previous": "yes"}
104157
)
105158
self.assertTrue(response.status_code, 200)
106159
self.assertEqual(response.headers["next_cursor"], "")
107160
self.assertEqual(
108161
response.json(),
109162
{
110163
"rows": [
111-
{"id": 1, "name": "Star Wars", "rating": 93},
164+
{
165+
"id": 1,
166+
"name": "Star Wars",
167+
"rating": 93,
168+
"director": 1,
169+
"readable": "George Lucas",
170+
},
171+
{
172+
"id": 2,
173+
"name": "Blade Runner",
174+
"rating": 90,
175+
"director": 2,
176+
"readable": "Ridley Scott",
177+
},
112178
]
113179
},
114180
)
@@ -118,9 +184,15 @@ def test_cursor_pagination_asc_previous_no_more_results(self):
118184
If cursor is empty and previous is applied there is no
119185
more results, return empty rows
120186
"""
187+
Director.insert(
188+
Director(name="George Lucas"),
189+
Director(name="Ridley Scott"),
190+
).run_sync()
191+
121192
Movie.insert(
122-
Movie(name="Star Wars", rating=93),
123-
Movie(name="Lord of the Rings", rating=90),
193+
Movie(name="Star Wars", rating=93, director=1),
194+
Movie(name="Blade Runner", rating=90, director=2),
195+
Movie(name="Alien", rating=91, director=2),
124196
).run_sync()
125197

126198
client = TestClient(app)

0 commit comments

Comments
 (0)