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
1 change: 1 addition & 0 deletions mypy_django_plugin/errorcodes.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from mypy.errorcodes import ErrorCode

MANAGER_UNTYPED = ErrorCode("django-manager", "Untyped manager disallowed", "Django")
MANAGER_MISSING = ErrorCode("django-manager-missing", "Couldn't resolve related manager for model", "Django")
22 changes: 22 additions & 0 deletions mypy_django_plugin/transformers/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from mypy.types import TypedDictType, TypeOfAny

from mypy_django_plugin.django.context import DjangoContext
from mypy_django_plugin.errorcodes import MANAGER_MISSING
from mypy_django_plugin.lib import fullnames, helpers
from mypy_django_plugin.lib.fullnames import ANNOTATIONS_FULLNAME, ANY_ATTR_ALLOWED_CLASS_FULLNAME, MODEL_CLASS_FULLNAME
from mypy_django_plugin.lib.helpers import add_new_class_for_module
Expand Down Expand Up @@ -341,6 +342,7 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
continue

if isinstance(relation, (ManyToOneRel, ManyToManyRel)):
related_manager_info = None
try:
related_manager_info = self.lookup_typeinfo_or_incomplete_defn_error(
fullnames.RELATED_MANAGER_CLASS
Expand All @@ -352,6 +354,26 @@ def run_with_model_cls(self, model_cls: Type[Model]) -> None:
if not self.api.final_iteration:
raise exc
else:
if related_manager_info:
"""
If a django model has a Manager class that cannot be
resolved statically (if it is generated in a way
where we cannot import it, like `objects = my_manager_factory()`),
we fallback to the default related manager, so you
at least get a base level of working type checking.

See https://github.com/typeddjango/django-stubs/pull/993
for more information on when this error can occur.
"""
self.add_new_node_to_model_class(
attname, Instance(related_manager_info, [Instance(related_model_info, [])])
)
related_model_fullname = related_model_cls.__module__ + "." + related_model_cls.__name__
self.ctx.api.fail(
f"Couldn't resolve related manager for relation {relation.name!r} (from {related_model_fullname}.{relation.field}).",
self.ctx.cls,
code=MANAGER_MISSING,
)
continue

# Check if the related model has a related manager subclassed from the default manager
Expand Down
36 changes: 36 additions & 0 deletions tests/typecheck/fields/test_related.yml
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,42 @@
pass


- case: test_related_managers_when_manager_is_dynamically_generated_and_cannot_be_imported
main: |
from myapp import models
installed_apps:
- myapp
files:
- path: myapp/__init__.py
- path: myapp/models.py
content: |
from django.db import models

class User(models.Model):
name = models.TextField()

def DynamicManager() -> models.Manager:
class InnerManager(models.Manager):
def some_method(self, arg: str) -> None:
return None

return InnerManager()

class Booking(models.Model):
renter = models.ForeignKey(User, on_delete=models.PROTECT)
owner = models.ForeignKey(User, on_delete=models.PROTECT, related_name='bookingowner_set')

objects = DynamicManager()

def process_booking(user: User):
reveal_type(user.bookingowner_set)
reveal_type(user.booking_set)
out: |
myapp/models:3: error: Couldn't resolve related manager for relation 'booking' (from myapp.models.Booking.myapp.Booking.renter).
myapp/models:3: error: Couldn't resolve related manager for relation 'bookingowner_set' (from myapp.models.Booking.myapp.Booking.owner).
myapp/models:20: note: Revealed type is "django.db.models.manager.RelatedManager[myapp.models.Booking]"
myapp/models:21: note: Revealed type is "django.db.models.manager.RelatedManager[myapp.models.Booking]"

- case: foreign_key_relationship_for_models_with_custom_manager
main: |
from myapp.models import Transaction
Expand Down