From b62780801e1d00c4764cf4f452abf9ff9408a4e3 Mon Sep 17 00:00:00 2001 From: Artur Mostowski Date: Tue, 18 Nov 2025 18:34:06 +0100 Subject: [PATCH 1/3] make code in contrib/admin/filters.pyi properly use generics --- django-stubs/contrib/admin/filters.pyi | 52 ++++++++++++++------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/django-stubs/contrib/admin/filters.pyi b/django-stubs/contrib/admin/filters.pyi index 6502ae210..a398130b1 100644 --- a/django-stubs/contrib/admin/filters.pyi +++ b/django-stubs/contrib/admin/filters.pyi @@ -1,6 +1,6 @@ from collections.abc import Callable, Iterable, Iterator from datetime import date, datetime -from typing import Any, ClassVar +from typing import Any, ClassVar, Generic, TypeVar from django.contrib.admin.options import ModelAdmin from django.contrib.admin.views.main import ChangeList @@ -21,59 +21,63 @@ class _ListFilterChoices(TypedDict): query_string: str display: _StrOrPromise -class ListFilter: +_ModelT = TypeVar("_ModelT", bound=Model) +_ST = TypeVar("_ST", contravariant=True) +_GT = TypeVar("_GT", covariant=True) + +class ListFilter(Generic[_ModelT]): title: _StrOrPromise | None template: str request: HttpRequest used_parameters: dict[str, bool | datetime | str] def __init__( - self, request: HttpRequest, params: dict[str, str], model: type[Model], model_admin: ModelAdmin + self, request: HttpRequest, params: dict[str, str], model: type[Model], model_admin: ModelAdmin[_ModelT] ) -> None: ... def has_output(self) -> bool: ... def choices(self, changelist: ChangeList) -> Iterator[_ListFilterChoices]: ... - def queryset(self, request: HttpRequest, queryset: QuerySet) -> QuerySet | None: ... + def queryset(self, request: HttpRequest, queryset: QuerySet[_ModelT]) -> QuerySet[_ModelT] | None: ... def expected_parameters(self) -> list[str | None]: ... class FacetsMixin: def get_facet_counts(self, pk_attname: str, filtered_qs: QuerySet[Model]) -> dict[str, Count]: ... def get_facet_queryset(self, changelist: ChangeList) -> dict[str, int]: ... -class SimpleListFilter(FacetsMixin, ListFilter): +class SimpleListFilter(FacetsMixin, ListFilter[_ModelT]): parameter_name: str | None lookup_choices: list[tuple[str, _StrOrPromise]] def value(self) -> str | None: ... - def lookups(self, request: HttpRequest, model_admin: ModelAdmin) -> Iterable[tuple[str, _StrOrPromise]] | None: ... + def lookups(self, request: HttpRequest, model_admin: ModelAdmin[_ModelT]) -> Iterable[tuple[str, _StrOrPromise]] | None: ... -class FieldListFilter(FacetsMixin, ListFilter): +class FieldListFilter(FacetsMixin, ListFilter[_ModelT], Generic[_ModelT, _ST, _GT]): list_separator: ClassVar[str] - field: Field + field: Field[_ST, _GT] field_path: str title: _StrOrPromise def __init__( self, - field: Field, + field: Field[_ST, _GT], request: HttpRequest, params: dict[str, str], model: type[Model], - model_admin: ModelAdmin, + model_admin: ModelAdmin[_ModelT], field_path: str, ) -> None: ... @classmethod def register( - cls, test: Callable[[Field], Any], list_filter_class: type[FieldListFilter], take_priority: bool = ... + cls, test: Callable[[Field[_ST, _GT]], Any], list_filter_class: type[FieldListFilter[_ModelT, _ST, _GT]], take_priority: bool = ... ) -> None: ... @classmethod def create( cls, - field: Field, + field: Field[_ST, _GT], request: HttpRequest, params: dict[str, str], model: type[Model], - model_admin: ModelAdmin, + model_admin: ModelAdmin[_ModelT], field_path: str, - ) -> FieldListFilter: ... + ) -> FieldListFilter[_ModelT, _ST, _GT]: ... -class RelatedFieldListFilter(FieldListFilter): +class RelatedFieldListFilter(FieldListFilter[_ModelT, _ST, _GT]): lookup_kwarg: str lookup_kwarg_isnull: str lookup_val: str | None @@ -84,25 +88,25 @@ class RelatedFieldListFilter(FieldListFilter): @property def include_empty_choice(self) -> bool: ... def field_admin_ordering( - self, field: RelatedField, request: HttpRequest, model_admin: ModelAdmin + self, field: RelatedField[_ST, _GT], request: HttpRequest, model_admin: ModelAdmin[_ModelT] ) -> _ListOrTuple[str]: ... def field_choices( - self, field: RelatedField, request: HttpRequest, model_admin: ModelAdmin + self, field: RelatedField[_ST, _GT], request: HttpRequest, model_admin: ModelAdmin[_ModelT] ) -> list[tuple[str, _StrOrPromise]]: ... -class BooleanFieldListFilter(FieldListFilter): +class BooleanFieldListFilter(FieldListFilter[_ModelT, _ST, _GT]): lookup_kwarg: str lookup_kwarg2: str lookup_val: str | None lookup_val2: str | None -class ChoicesFieldListFilter(FieldListFilter): +class ChoicesFieldListFilter(FieldListFilter[_ModelT, _ST, _GT]): lookup_kwarg: str lookup_kwarg_isnull: str lookup_val: str | None lookup_val_isnull: str | None -class DateFieldListFilter(FieldListFilter): +class DateFieldListFilter(FieldListFilter[_ModelT, _ST, _GT]): field_generic: str date_params: dict[str, str] lookup_kwarg_since: str @@ -110,17 +114,17 @@ class DateFieldListFilter(FieldListFilter): links: tuple[tuple[_StrOrPromise, dict[str, bool | date | datetime]], ...] lookup_kwarg_isnull: str -class AllValuesFieldListFilter(FieldListFilter): +class AllValuesFieldListFilter(FieldListFilter[_ModelT, _ST, _GT]): lookup_kwarg: str lookup_kwarg_isnull: str lookup_val: str | None lookup_val_isnull: str | None empty_value_display: SafeString - lookup_choices: QuerySet + lookup_choices: QuerySet[_ModelT] -class RelatedOnlyFieldListFilter(RelatedFieldListFilter): ... +class RelatedOnlyFieldListFilter(RelatedFieldListFilter[_ModelT, _ST, _GT]): ... -class EmptyFieldListFilter(FieldListFilter): +class EmptyFieldListFilter(FieldListFilter[_ModelT, _ST, _GT]): lookup_kwarg: str lookup_val: str | None def get_lookup_condition(self) -> Q: ... From e66df9a06e202a6b27e9052bad25b9940024137d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 17:35:51 +0000 Subject: [PATCH 2/3] [pre-commit.ci] auto fixes from pre-commit.com hooks --- django-stubs/contrib/admin/filters.pyi | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/django-stubs/contrib/admin/filters.pyi b/django-stubs/contrib/admin/filters.pyi index a398130b1..d781ef1f0 100644 --- a/django-stubs/contrib/admin/filters.pyi +++ b/django-stubs/contrib/admin/filters.pyi @@ -46,7 +46,9 @@ class SimpleListFilter(FacetsMixin, ListFilter[_ModelT]): parameter_name: str | None lookup_choices: list[tuple[str, _StrOrPromise]] def value(self) -> str | None: ... - def lookups(self, request: HttpRequest, model_admin: ModelAdmin[_ModelT]) -> Iterable[tuple[str, _StrOrPromise]] | None: ... + def lookups( + self, request: HttpRequest, model_admin: ModelAdmin[_ModelT] + ) -> Iterable[tuple[str, _StrOrPromise]] | None: ... class FieldListFilter(FacetsMixin, ListFilter[_ModelT], Generic[_ModelT, _ST, _GT]): list_separator: ClassVar[str] @@ -64,7 +66,10 @@ class FieldListFilter(FacetsMixin, ListFilter[_ModelT], Generic[_ModelT, _ST, _G ) -> None: ... @classmethod def register( - cls, test: Callable[[Field[_ST, _GT]], Any], list_filter_class: type[FieldListFilter[_ModelT, _ST, _GT]], take_priority: bool = ... + cls, + test: Callable[[Field[_ST, _GT]], Any], + list_filter_class: type[FieldListFilter[_ModelT, _ST, _GT]], + take_priority: bool = ..., ) -> None: ... @classmethod def create( From 71da8d1f3dbb76b853f9f19b00170b506ae05e1a Mon Sep 17 00:00:00 2001 From: Artur Mostowski Date: Tue, 18 Nov 2025 19:32:49 +0100 Subject: [PATCH 3/3] one more _ModelT --- django-stubs/contrib/admin/filters.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/django-stubs/contrib/admin/filters.pyi b/django-stubs/contrib/admin/filters.pyi index d781ef1f0..167f1ec20 100644 --- a/django-stubs/contrib/admin/filters.pyi +++ b/django-stubs/contrib/admin/filters.pyi @@ -31,7 +31,7 @@ class ListFilter(Generic[_ModelT]): request: HttpRequest used_parameters: dict[str, bool | datetime | str] def __init__( - self, request: HttpRequest, params: dict[str, str], model: type[Model], model_admin: ModelAdmin[_ModelT] + self, request: HttpRequest, params: dict[str, str], model: type[_ModelT], model_admin: ModelAdmin[_ModelT] ) -> None: ... def has_output(self) -> bool: ... def choices(self, changelist: ChangeList) -> Iterator[_ListFilterChoices]: ...