Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement last for querysets and models #1756

Merged
merged 4 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Contributors
* Abdeldjalil Hezouat ``@Abdeldjalil-H``
* Andrea Magistà ``@vlakius``
* Daniel Szucs ``@Quasar6X``
* Rui Catarino ``@ruitcatarino``

Special Thanks
==============
Expand Down
4 changes: 4 additions & 0 deletions tests/test_model_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,10 @@ async def test_first(self):
mdl = await self.cls.first()
self.assertEqual(self.mdl.id, mdl.id)

async def test_last(self):
mdl = await self.cls.last()
self.assertEqual(self.mdl.id, mdl.id)

async def test_latest(self):
mdl = await self.cls.latest("name")
self.assertEqual(self.mdl.id, mdl.id)
Expand Down
29 changes: 29 additions & 0 deletions tests/test_queryset.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,35 @@ async def test_first(self):
None,
)

async def test_last(self):
self.assertEqual(
(await IntFields.all().order_by("intnum").filter(intnum__gte=40).last()).intnum, 97
)
self.assertEqual(
(await IntFields.all().order_by("intnum").filter(intnum__gte=40).last().values())[
"intnum"
],
97,
)
self.assertEqual(
(await IntFields.all().order_by("intnum").filter(intnum__gte=40).last().values_list())[
1
],
97,
)

self.assertEqual(
await IntFields.all().order_by("intnum").filter(intnum__gte=400).last(), None
)
self.assertEqual(
await IntFields.all().order_by("intnum").filter(intnum__gte=400).last().values(), None
)
self.assertEqual(
await IntFields.all().order_by("intnum").filter(intnum__gte=400).last().values_list(),
None,
)
self.assertEqual((await IntFields.all().filter(intnum__gte=40).last()).intnum, 97)

async def test_latest(self):
self.assertEqual((await IntFields.all().latest("intnum")).intnum, 97)
self.assertEqual(
Expand Down
7 changes: 7 additions & 0 deletions tortoise/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,13 @@ def first(cls, using_db: Optional[BaseDBAsyncClient] = None) -> QuerySetSingle[O
"""
return cls._db_queryset(using_db).first()

@classmethod
def last(cls, using_db: Optional[BaseDBAsyncClient] = None) -> QuerySetSingle[Optional[Self]]:
"""
Generates a QuerySet that returns the last record.
"""
return cls._db_queryset(using_db).last()

@classmethod
def filter(cls, *args: Q, **kwargs: Any) -> QuerySet[Self]:
"""
Expand Down
23 changes: 23 additions & 0 deletions tortoise/queryset.py
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,29 @@ def first(self) -> QuerySetSingle[Optional[MODEL]]:
queryset._single = True
return queryset # type: ignore

def last(self) -> QuerySetSingle[Optional[MODEL]]:
"""
Limit queryset to one object and return the last object instead of list.
"""
queryset = self._clone()

if queryset._orderings:
new_ordering = [
(field, Order.desc if order_type == Order.asc else Order.asc)
for field, order_type in queryset._orderings
]
elif pk := self.model._meta.pk:
new_ordering = [(pk.model_field_name, Order.desc)]
else:
raise FieldError(
f"QuerySet has no ordering and model {self.model.__name__} has no pk defined"
)

queryset._orderings = new_ordering
queryset._limit = 1
queryset._single = True
return queryset # type: ignore

def get(self, *args: Q, **kwargs: Any) -> QuerySetSingle[MODEL]:
"""
Fetch exactly one object matching the parameters.
Expand Down