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)