From 0fb447e88086ea81eec47c3567f9830af7ffb43b Mon Sep 17 00:00:00 2001 From: David Winterbottom Date: Tue, 29 Mar 2022 10:03:29 +0100 Subject: [PATCH] Extend return type of `ListView.get_queryset` Which can return either a `QuerySet` or an iterable. See, e.g. https://github.com/django/django/blob/83c803f161044fbfbfcd9a0c94ca93dc131be662/django/views/generic/list.py#L26-L27 This has always been true for the generic `ListView` class. --- django-stubs/views/generic/list.pyi | 4 ++-- tests/typecheck/views/generic/test_list.yml | 25 ++++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/django-stubs/views/generic/list.pyi b/django-stubs/views/generic/list.pyi index 0775a13d8..1177101ae 100644 --- a/django-stubs/views/generic/list.pyi +++ b/django-stubs/views/generic/list.pyi @@ -1,4 +1,4 @@ -from typing import Any, Generic, Optional, Sequence, Tuple, Type, TypeVar +from typing import Any, Generic, Optional, Sequence, Tuple, Type, TypeVar, Union, Iterable from django.core.paginator import Paginator from django.db.models import Model @@ -18,7 +18,7 @@ class MultipleObjectMixin(Generic[T], ContextMixin): paginator_class: Type[Paginator] = ... page_kwarg: str = ... ordering: Sequence[str] = ... - def get_queryset(self) -> QuerySet[T]: ... + def get_queryset(self) -> Union[QuerySet[T], Iterable[T]]: ... def get_ordering(self) -> Sequence[str]: ... def paginate_queryset(self, queryset: QuerySet, page_size: int) -> Tuple[Paginator, int, QuerySet[T], bool]: ... def get_paginate_by(self, queryset: QuerySet) -> Optional[int]: ... diff --git a/tests/typecheck/views/generic/test_list.yml b/tests/typecheck/views/generic/test_list.yml index 2ef0301c7..bbb533834 100644 --- a/tests/typecheck/views/generic/test_list.yml +++ b/tests/typecheck/views/generic/test_list.yml @@ -21,6 +21,29 @@ class MyModel(models.Model): ... +- case: nonqueryset_list_view + main: | + from typing import List + + from django.views.generic import ListView + from django.db.models import QuerySet + + from myapp.models import MyModel + + class MyListView(ListView): + model = MyModel + + def get_queryset(self) -> List[MyModel]: + ... + custom_settings: | + INSTALLED_APPS = ('myapp',) + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from django.db import models + class MyModel(models.Model): + ... - case: generic_list_view_wrong main: | @@ -49,4 +72,4 @@ out: | main:7: error: Incompatible types in assignment (expression has type "Type[MyModel]", base class "MultipleObjectMixin" defined the type as "Optional[Type[Other]]") main:8: error: Incompatible types in assignment (expression has type "_QuerySet[MyModel, MyModel]", base class "MultipleObjectMixin" defined the type as "Optional[_QuerySet[Other, Other]]") - main:10: error: Return type "_QuerySet[MyModel, MyModel]" of "get_queryset" incompatible with return type "_QuerySet[Other, Other]" in supertype "MultipleObjectMixin" + main:10: error: Return type "_QuerySet[MyModel, MyModel]" of "get_queryset" incompatible with return type "Iterable[Other]" in supertype "MultipleObjectMixin"