From d6010fd95187a6ec371f5bc28daeb8337974d4c5 Mon Sep 17 00:00:00 2001 From: Petter Friberg <petter_friberg@hotmail.com> Date: Thu, 25 Jul 2024 21:05:49 +0200 Subject: [PATCH] Check calls to filtering manager methods involving `ManyToManyField` (#2275) --- mypy_django_plugin/django/context.py | 2 +- mypy_django_plugin/transformers/models.py | 1 + tests/typecheck/fields/test_related.yml | 26 +++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/mypy_django_plugin/django/context.py b/mypy_django_plugin/django/context.py index 146ca368e..75e0f96a6 100644 --- a/mypy_django_plugin/django/context.py +++ b/mypy_django_plugin/django/context.py @@ -132,7 +132,7 @@ def __init__(self, django_settings_module: str) -> None: def model_modules(self) -> Dict[str, Dict[str, Type[Model]]]: """All modules that contain Django models.""" modules: Dict[str, Dict[str, Type[Model]]] = defaultdict(dict) - for concrete_model_cls in self.apps_registry.get_models(): + for concrete_model_cls in self.apps_registry.get_models(include_auto_created=True, include_swapped=True): modules[concrete_model_cls.__module__][concrete_model_cls.__name__] = concrete_model_cls # collect abstract=True models for model_cls in concrete_model_cls.mro()[1:]: diff --git a/mypy_django_plugin/transformers/models.py b/mypy_django_plugin/transformers/models.py index c203bc8c9..0ade78630 100644 --- a/mypy_django_plugin/transformers/models.py +++ b/mypy_django_plugin/transformers/models.py @@ -932,6 +932,7 @@ def create_many_related_manager(self, model: Instance) -> None: helpers.set_many_to_many_manager_info( to=model.type, derived_from="_default_manager", manager_info=related_manager_info ) + helpers.add_new_manager_base(self.api, related_manager_info.fullname) class MetaclassAdjustments(ModelClassInitializer): diff --git a/tests/typecheck/fields/test_related.yml b/tests/typecheck/fields/test_related.yml index 9a19e796d..983c3b241 100644 --- a/tests/typecheck/fields/test_related.yml +++ b/tests/typecheck/fields/test_related.yml @@ -1472,3 +1472,29 @@ class Second(Parent): ... + +- case: test_m2m_models_manager_filter_kwargs_checked + main: | + from myapp.models import MyModel, Other + MyModel.objects.filter(xyz__isnull=False) # E: Cannot resolve keyword 'xyz' into field. Choices are: id, others [misc] + MyModel.objects.get(xyz__isnull=False) # E: Cannot resolve keyword 'xyz' into field. Choices are: id, others [misc] + MyModel.objects.exclude(xyz__isnull=False) # E: Cannot resolve keyword 'xyz' into field. Choices are: id, others [misc] + other = Other() + other.mymodel_set.filter(xyz__isnull=True) # E: Cannot resolve keyword 'xyz' into field. Choices are: id, mymodel, mymodel_id, other, other_id [misc] + other.mymodel_set.get(xyz__isnull=True) # E: Cannot resolve keyword 'xyz' into field. Choices are: id, mymodel, mymodel_id, other, other_id [misc] + other.mymodel_set.exclude(xyz__isnull=True) # E: Cannot resolve keyword 'xyz' into field. Choices are: id, mymodel, mymodel_id, other, other_id [misc] + MyModel.others.through.objects.filter(xyz__isnull=False) # E: Cannot resolve keyword 'xyz' into field. Choices are: id, mymodel, mymodel_id, other, other_id [misc] + MyModel.others.through.objects.get(xyz__isnull=False) # E: Cannot resolve keyword 'xyz' into field. Choices are: id, mymodel, mymodel_id, other, other_id [misc] + MyModel.others.through.objects.exclude(xyz__isnull=False) # E: Cannot resolve keyword 'xyz' into field. Choices are: id, mymodel, mymodel_id, other, other_id [misc] + installed_apps: + - myapp + files: + - path: myapp/__init__.py + - path: myapp/models.py + content: | + from django.db import models + class Other(models.Model): + ... + + class MyModel(models.Model): + others = models.ManyToManyField(Other)