Skip to content

Commit b564029

Browse files
update add_ordering signature (#2822)
1 parent 4ea94ea commit b564029

File tree

4 files changed

+66
-8
lines changed

4 files changed

+66
-8
lines changed

django-stubs/db/models/query.pyi

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ _PrefetchedQuerySetT = TypeVar("_PrefetchedQuerySetT", bound=QuerySet[Model], co
2424
# This will be specialized to a `LiteralString` in the plugin for further processing and validation
2525
_ToAttrT = TypeVar("_ToAttrT", bound=str, covariant=True, default=str)
2626

27+
_OrderByFieldName: TypeAlias = str | Combinable
28+
2729
MAX_GET_RESULTS: int
2830
REPR_OUTPUT_SIZE: int
2931

@@ -189,7 +191,7 @@ class QuerySet(Generic[_Model, _Row], Iterable[_Row], Sized):
189191
def prefetch_related(self, *lookups: str | Prefetch[_LookupT, _PrefetchedQuerySetT, _ToAttrT]) -> Self: ...
190192
def annotate(self, *args: Any, **kwargs: Any) -> Self: ...
191193
def alias(self, *args: Any, **kwargs: Any) -> Self: ...
192-
def order_by(self, *field_names: str | Combinable) -> Self: ...
194+
def order_by(self, *field_names: _OrderByFieldName) -> Self: ...
193195
def distinct(self, *field_names: str) -> Self: ...
194196
# extra() return type won't be supported any time soon
195197
def extra(
@@ -198,7 +200,7 @@ class QuerySet(Generic[_Model, _Row], Iterable[_Row], Sized):
198200
where: Sequence[str] | None = None,
199201
params: Sequence[Any] | None = None,
200202
tables: Sequence[str] | None = None,
201-
order_by: Sequence[str] | None = None,
203+
order_by: Sequence[_OrderByFieldName] | None = None,
202204
select_params: Sequence[Any] | None = None,
203205
) -> QuerySet[Any, Any]: ...
204206
def reverse(self) -> Self: ...

django-stubs/db/models/sql/query.pyi

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ from typing import Any, Literal, NamedTuple
44

55
from django.db.backends.utils import CursorWrapper
66
from django.db.models import Field, FilteredRelation, Model, Q
7-
from django.db.models.expressions import BaseExpression, Combinable, Expression, OrderBy
7+
from django.db.models.expressions import BaseExpression, Combinable, Expression
88
from django.db.models.lookups import Lookup, Transform
99
from django.db.models.options import Options
10+
from django.db.models.query import _OrderByFieldName
1011
from django.db.models.query_utils import PathInfo
1112
from django.db.models.sql.datastructures import BaseTable, Join
1213
from django.db.models.sql.where import WhereNode
@@ -58,7 +59,7 @@ class Query(BaseExpression):
5859
filter_is_sticky: bool
5960
subquery: bool
6061
group_by: None | Sequence[Combinable] | Sequence[str] | Literal[True]
61-
order_by: Sequence[Any]
62+
order_by: Sequence[_OrderByFieldName]
6263
distinct: bool
6364
distinct_fields: tuple[str, ...]
6465
select: Sequence[BaseExpression]
@@ -78,7 +79,7 @@ class Query(BaseExpression):
7879
combined_queries: tuple
7980
extra_select_mask: set[str] | None
8081
extra_tables: tuple
81-
extra_order_by: Sequence[Any]
82+
extra_order_by: Sequence[_OrderByFieldName]
8283
deferred_loading: tuple[set[str] | frozenset[str], bool]
8384
explain_query: bool
8485
explain_format: str | None
@@ -190,7 +191,7 @@ class Query(BaseExpression):
190191
def set_select(self, cols: list[Expression]) -> None: ...
191192
def add_distinct_fields(self, *field_names: str) -> None: ...
192193
def add_fields(self, field_names: Iterable[str], allow_m2m: bool = True) -> None: ...
193-
def add_ordering(self, *ordering: str | OrderBy) -> None: ...
194+
def add_ordering(self, *ordering: _OrderByFieldName) -> None: ...
194195
def clear_where(self) -> None: ...
195196
def clear_ordering(self, force: bool = False, clear_default: bool = True) -> None: ...
196197
def set_group_by(self, allow_aliases: bool = True) -> None: ...
@@ -202,7 +203,7 @@ class Query(BaseExpression):
202203
where: Sequence[str] | None,
203204
params: Sequence[str] | None,
204205
tables: Sequence[str] | None,
205-
order_by: Sequence[str] | None,
206+
order_by: Sequence[_OrderByFieldName] | None,
206207
) -> None: ...
207208
def clear_deferred_loading(self) -> None: ...
208209
def add_deferred_loading(self, field_names: Iterable[str]) -> None: ...
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
from django.contrib.auth.models import User
2+
from django.db.models import F, OrderBy
3+
from django.db.models.sql.query import Query
4+
5+
qs = User.objects.all()
6+
qs.order_by("username")
7+
qs.order_by(F("username"))
8+
qs.order_by("-date_joined", F("last_login").desc())
9+
10+
# Test OrderBy expressions (success cases)
11+
qs.order_by(OrderBy(F("username")))
12+
qs.order_by(OrderBy(F("date_joined"), descending=True))
13+
qs.order_by("username", OrderBy(F("last_login"), descending=True))
14+
15+
qs.extra(order_by=["username"])
16+
qs.extra(order_by=[F("username")])
17+
qs.extra(order_by=["username", F("date_joined").desc()])
18+
qs.extra(order_by=[OrderBy(F("username"))])
19+
qs.extra(order_by=["username", OrderBy(F("date_joined"), descending=True)])
20+
21+
query = Query(User)
22+
query.add_ordering("username")
23+
query.add_ordering(F("date_joined"))
24+
query.add_ordering("-last_login", F("username").desc())
25+
query.add_ordering(OrderBy(F("username")))
26+
query.add_ordering("username", OrderBy(F("date_joined"), descending=True))
27+
28+
query.add_extra(
29+
select=None,
30+
select_params=None,
31+
where=None,
32+
params=None,
33+
tables=None,
34+
order_by=["username", F("date_joined").desc(), OrderBy(F("last_login"))],
35+
)
36+
37+
# failure cases
38+
qs.order_by(123) # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
39+
qs.order_by(["username"]) # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
40+
qs.order_by({"username": "asc"}) # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
41+
42+
qs.extra(order_by=[123]) # type: ignore[list-item] # pyright: ignore[reportArgumentType]
43+
qs.extra(order_by=["username", 456]) # type: ignore[list-item] # pyright: ignore[reportArgumentType]
44+
45+
query.add_ordering(123) # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
46+
query.add_ordering(["username"]) # type: ignore[arg-type] # pyright: ignore[reportArgumentType]
47+
48+
query.add_extra(
49+
select=None,
50+
select_params=None,
51+
where=None,
52+
params=None,
53+
tables=None,
54+
order_by=[123, "username"], # type: ignore[list-item] # pyright: ignore[reportArgumentType]
55+
)

tests/typecheck/managers/querysets/test_from_queryset.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@
668668
reveal_type(MyModel.objects.difference) # N: Revealed type is "def (*other_qs: django.db.models.query.QuerySet[django.db.models.base.Model, django.db.models.base.Model]) -> myapp.models.MyQuerySet"
669669
reveal_type(MyModel.objects.distinct) # N: Revealed type is "def (*field_names: builtins.str) -> myapp.models.MyQuerySet"
670670
reveal_type(MyModel.objects.exclude) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet"
671-
reveal_type(MyModel.objects.extra) # N: Revealed type is "def (select: builtins.dict[builtins.str, Any] | None =, where: typing.Sequence[builtins.str] | None =, params: typing.Sequence[Any] | None =, tables: typing.Sequence[builtins.str] | None =, order_by: typing.Sequence[builtins.str] | None =, select_params: typing.Sequence[Any] | None =) -> myapp.models.MyQuerySet"
671+
reveal_type(MyModel.objects.extra) # N: Revealed type is "def (select: builtins.dict[builtins.str, Any] | None =, where: typing.Sequence[builtins.str] | None =, params: typing.Sequence[Any] | None =, tables: typing.Sequence[builtins.str] | None =, order_by: typing.Sequence[builtins.str | django.db.models.expressions.Combinable] | None =, select_params: typing.Sequence[Any] | None =) -> myapp.models.MyQuerySet"
672672
reveal_type(MyModel.objects.filter) # N: Revealed type is "def (*args: Any, **kwargs: Any) -> myapp.models.MyQuerySet"
673673
reveal_type(MyModel.objects.intersection) # N: Revealed type is "def (*other_qs: django.db.models.query.QuerySet[django.db.models.base.Model, django.db.models.base.Model]) -> myapp.models.MyQuerySet"
674674
reveal_type(MyModel.objects.none) # N: Revealed type is "def () -> myapp.models.MyQuerySet"

0 commit comments

Comments
 (0)