Skip to content
This repository was archived by the owner on Aug 19, 2025. It is now read-only.

Commit 62433de

Browse files
AndTheDaysGoByaminalaee
andauthored
implement order_by (#70)
Co-authored-by: Amin Alaee <[email protected]>
1 parent ec54759 commit 62433de

File tree

3 files changed

+66
-0
lines changed

3 files changed

+66
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ notes = await Note.objects.exclude(completed=False).all()
8888
# exact, iexact, contains, icontains, lt, lte, gt, gte, in
8989
notes = await Note.objects.filter(text__icontains="mum").all()
9090

91+
# .order_by()
92+
# order by ascending name and descending id
93+
notes = await Note.objects.order_by("name", "-id").all()
94+
9195
# .get()
9296
note = await Note.objects.get(id=1)
9397

@@ -100,6 +104,7 @@ await note.delete()
100104
# 'pk' always refers to the primary key
101105
note = await Note.objects.get(pk=2)
102106
note.pk # 2
107+
103108
```
104109

105110
ORM supports loading and filtering across foreign keys...

orm/models.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,14 @@ def __init__(
5656
select_related=None,
5757
limit_count=None,
5858
offset=None,
59+
order_by=None,
5960
):
6061
self.model_cls = model_cls
6162
self.filter_clauses = [] if filter_clauses is None else filter_clauses
6263
self._select_related = [] if select_related is None else select_related
6364
self.limit_count = limit_count
6465
self.query_offset = offset
66+
self._order_by = [] if order_by is None else order_by
6567

6668
def __get__(self, instance, owner):
6769
return self.__class__(model_cls=owner)
@@ -96,6 +98,10 @@ def build_select_expression(self):
9698
clause = sqlalchemy.sql.and_(*self.filter_clauses)
9799
expr = expr.where(clause)
98100

101+
if self._order_by:
102+
order_by = list(map(self._prepare_order_by, self._order_by))
103+
expr = expr.order_by(*order_by)
104+
99105
if self.limit_count:
100106
expr = expr.limit(self.limit_count)
101107

@@ -186,6 +192,7 @@ def _filter_query(self, _exclude: bool = False, **kwargs):
186192
select_related=select_related,
187193
limit_count=self.limit_count,
188194
offset=self.query_offset,
195+
order_by=self._order_by,
189196
)
190197

191198
def select_related(self, related):
@@ -199,6 +206,17 @@ def select_related(self, related):
199206
select_related=related,
200207
limit_count=self.limit_count,
201208
offset=self.query_offset,
209+
order_by=self._order_by,
210+
)
211+
212+
def order_by(self, *order_by):
213+
return self.__class__(
214+
model_cls=self.model_cls,
215+
filter_clauses=self.filter_clauses,
216+
select_related=self._select_related,
217+
limit_count=self.limit_count,
218+
offset=self.query_offset,
219+
order_by=order_by,
202220
)
203221

204222
async def exists(self) -> bool:
@@ -213,6 +231,7 @@ def limit(self, limit_count: int):
213231
select_related=self._select_related,
214232
limit_count=limit_count,
215233
offset=self.query_offset,
234+
order_by=self._order_by,
216235
)
217236

218237
def offset(self, offset: int):
@@ -222,6 +241,7 @@ def offset(self, offset: int):
222241
select_related=self._select_related,
223242
limit_count=self.limit_count,
224243
offset=offset,
244+
order_by=self._order_by,
225245
)
226246

227247
async def count(self) -> int:
@@ -285,6 +305,12 @@ async def create(self, **kwargs):
285305
instance.pk = await self.database.execute(expr)
286306
return instance
287307

308+
def _prepare_order_by(self, order_by: str):
309+
reverse = order_by.startswith("-")
310+
order_by = order_by.lstrip("-")
311+
order_col = self.table.columns[order_by]
312+
return order_col.desc() if reverse else order_col
313+
288314

289315
class Model(typesystem.Schema, metaclass=ModelMetaclass):
290316
__abstract__ = True

tests/test_models.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,41 @@ async def test_model_filter():
156156
assert await products.count() == 3
157157

158158

159+
async def test_model_order_by():
160+
async with database:
161+
await User.objects.create(name="Bob")
162+
await User.objects.create(name="Allen")
163+
await User.objects.create(name="Bob")
164+
165+
users = await User.objects.order_by("name").all()
166+
assert users[0].name == "Allen"
167+
assert users[1].name == "Bob"
168+
169+
users = await User.objects.order_by("-name").all()
170+
assert users[1].name == "Bob"
171+
assert users[2].name == "Allen"
172+
173+
users = await User.objects.order_by("name", "-id").all()
174+
assert users[0].name == "Allen"
175+
assert users[0].id == 2
176+
assert users[1].name == "Bob"
177+
assert users[1].id == 3
178+
179+
users = await User.objects.filter(name="Bob").order_by("-id").all()
180+
assert users[0].name == "Bob"
181+
assert users[0].id == 3
182+
assert users[1].name == "Bob"
183+
assert users[1].id == 1
184+
185+
users = await User.objects.order_by("id").limit(1).all()
186+
assert users[0].name == "Bob"
187+
assert users[0].id == 1
188+
189+
users = await User.objects.order_by("id").limit(1).offset(1).all()
190+
assert users[0].name == "Allen"
191+
assert users[0].id == 2
192+
193+
159194
async def test_model_exists():
160195
async with database:
161196
await User.objects.create(name="Tom")

0 commit comments

Comments
 (0)