Skip to content

Commit b76f9f4

Browse files
authored
Improve get_context_object_name on SingleObjectMixin and MultipleObjectMixin (#2298)
1 parent 9c156a0 commit b76f9f4

File tree

4 files changed

+48
-7
lines changed

4 files changed

+48
-7
lines changed

django-stubs/views/generic/detail.pyi

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Generic, TypeVar
1+
from typing import Any, Generic, TypeVar, overload
22

33
from django.db import models
44
from django.http import HttpRequest, HttpResponse
@@ -17,7 +17,10 @@ class SingleObjectMixin(Generic[_M], ContextMixin):
1717
def get_object(self, queryset: models.query.QuerySet[_M] | None = ...) -> _M: ...
1818
def get_queryset(self) -> models.query.QuerySet[_M]: ...
1919
def get_slug_field(self) -> str: ...
20-
def get_context_object_name(self, obj: _M) -> str | None: ...
20+
@overload
21+
def get_context_object_name(self, obj: _M) -> str: ...
22+
@overload
23+
def get_context_object_name(self, obj: Any) -> str | None: ...
2124

2225
class BaseDetailView(SingleObjectMixin[_M], View):
2326
object: _M

django-stubs/views/generic/list.pyi

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
from collections.abc import Sequence
2-
from typing import Any, Generic, TypeVar
2+
from typing import Any, Generic, Protocol, TypeVar, overload, type_check_only
33

44
from django.core.paginator import Page, Paginator, _SupportsPagination
55
from django.db.models import Model, QuerySet
66
from django.http import HttpRequest, HttpResponse
77
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View
88

9-
_M = TypeVar("_M", bound=Model, covariant=True)
9+
_M = TypeVar("_M", bound=Model)
10+
11+
@type_check_only
12+
class _HasModel(Protocol):
13+
@property
14+
def model(self) -> type[Model]: ...
1015

1116
class MultipleObjectMixin(Generic[_M], ContextMixin):
1217
allow_empty: bool
@@ -34,7 +39,10 @@ class MultipleObjectMixin(Generic[_M], ContextMixin):
3439
) -> Paginator: ...
3540
def get_paginate_orphans(self) -> int: ...
3641
def get_allow_empty(self) -> bool: ...
37-
def get_context_object_name(self, object_list: _SupportsPagination[_M]) -> str | None: ...
42+
@overload
43+
def get_context_object_name(self, object_list: _HasModel) -> str: ...
44+
@overload
45+
def get_context_object_name(self, object_list: Any) -> str | None: ...
3846
def get_context_data(
3947
self, *, object_list: _SupportsPagination[_M] | None = ..., **kwargs: Any
4048
) -> dict[str, Any]: ...

tests/assert_type/views/generic.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from typing import Optional, Type
2+
3+
from django.db import models
4+
from django.views.generic.detail import SingleObjectMixin
5+
from django.views.generic.list import ListView
6+
from typing_extensions import assert_type
7+
8+
9+
class MyModel(models.Model): ...
10+
11+
12+
class MyDetailView(SingleObjectMixin[MyModel]): ...
13+
14+
15+
detail_view = MyDetailView()
16+
assert_type(detail_view.model, Type[MyModel])
17+
assert_type(detail_view.queryset, Optional[models.QuerySet[MyModel, MyModel]])
18+
assert_type(detail_view.get_context_object_name(MyModel()), str)
19+
assert_type(detail_view.get_context_object_name(1), Optional[str])
20+
21+
22+
class MyListView(ListView[MyModel]): ...
23+
24+
25+
list_view = MyListView()
26+
assert_type(list_view.model, Optional[Type[MyModel]])
27+
assert_type(list_view.queryset, Optional[models.QuerySet[MyModel, MyModel]])
28+
assert_type(list_view.get_context_object_name(models.QuerySet[MyModel]()), str)
29+
assert_type(list_view.get_context_object_name(MyModel()), Optional[str])
30+
assert_type(list_view.get_context_object_name(1), Optional[str])

tests/typecheck/views/generic/test_detail.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
def get_queryset(self) -> QuerySet[MyModel]:
3838
self.get_object(super().get_queryset())
3939
return super().get_queryset()
40-
custom_settings: |
41-
INSTALLED_APPS = ('myapp',)
40+
installed_apps:
41+
- myapp
4242
files:
4343
- path: myapp/__init__.py
4444
- path: myapp/models.py

0 commit comments

Comments
 (0)