Skip to content
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
Empty file.
75 changes: 75 additions & 0 deletions tests/assert_type/db/models/test_manager_overrides/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Overriding `Manager.create()` with a narrowed return type.

Regression test for https://github.com/typeddjango/django-stubs/issues/578.
"""

from __future__ import annotations

from typing import Any

from django.db import models
from typing_extensions import assert_type, override

# ---------------------------------------------------------------------------
# Unparametrized manager: the plugin re-parametrizes it with the model it's on
# ---------------------------------------------------------------------------


class ArticleManager(models.Manager): # pyright: ignore[reportMissingTypeArgument]
# Without a type parameter, the inherited `create()` returns the unsolved `_T`, so narrowing it is an error.
# Only mypy checks the override against `_T`, the other type checkers treat it as unknown.
@override
def create(self, **kwargs: Any) -> Article: # type: ignore[override]
return super().create(**kwargs) # type: ignore[return-value] # pyright: ignore[reportUnknownVariableType]


class Article(models.Model):
objects = ArticleManager()


def override_manager_create_no_type_param() -> None:
assert_type(Article.objects.create(), Article)


# ---------------------------------------------------------------------------
# Manager parametrized with the model it's assigned to
# ---------------------------------------------------------------------------


class BookManager(models.Manager["Book"]):
@override
def create(self, **kwargs: Any) -> Book:
return super().create(**kwargs)


class Book(models.Model):
# pyrefly doesn't solve `Self` in the `objects: ClassVar[Manager[Self]]` declaration
objects = BookManager() # pyrefly: ignore[bad-assignment]


def override_manager_create_with_type_param() -> None:
assert_type(Book.objects.create(), Book)


# ---------------------------------------------------------------------------
# Manager parametrized with another model than the one it's assigned to
# ---------------------------------------------------------------------------


class ReviewManager(models.Manager["Author"]):
# The type parameter, not the model the manager ends up on, drives what the inherited `create()` returns.
@override
def create(self, **kwargs: Any) -> Review: # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] # pyrefly: ignore[bad-override] # ty: ignore[invalid-method-override]
return super().create(**kwargs) # type: ignore[return-value] # pyright: ignore[reportReturnType] # pyrefly: ignore[bad-return] # ty: ignore[invalid-return-type]


class Review(models.Model):
objects = ReviewManager() # pyright: ignore[reportGeneralTypeIssues] # pyrefly: ignore[bad-assignment]


class Author(models.Model):
pass


def override_manager_create_with_incoherent_type_param() -> None:
assert_type(Review.objects.create(), Review)
48 changes: 0 additions & 48 deletions tests/typecheck/managers/test_managers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -401,54 +401,6 @@
movie = models.TextField()


- case: override_manager_create1
main: |
from myapp.models import MyModel
installed_apps:
- myapp
out: |
myapp/models:7: error: Return type "MyModel" of "create" incompatible with return type "_T" in supertype "django.db.models.manager.Manager" [override]
myapp/models:8: error: Incompatible return value type (got "_T", expected "MyModel") [return-value]
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from typing import Any
from django.db import models
from typing_extensions import override

class MyModelManager(models.Manager):
@override
def create(self, **kwargs: Any) -> 'MyModel':
return super().create(**kwargs)


class MyModel(models.Model):
objects = MyModelManager()


- case: override_manager_create2
main: |
from myapp.models import MyModel
MyModel.objects.create()
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from typing import Any
from django.db import models
from typing_extensions import override

class MyModelManager(models.Manager['MyModel']):
@override
def create(self, **kwargs: Any) -> 'MyModel':
return super().create(**kwargs)

class MyModel(models.Model):
objects = MyModelManager()

- case: regression_manager_scope_foreign
main: |
from typing_extensions import reveal_type
Expand Down
Loading