Repository: typeddjango/django-stubs

Issue:
Custom queryset methods returns `Any` on cached runs
# Bug report

I have a manager with custom queryset methods getting return types of `Any`, on cached runs.

## What's wrong

I've investigated the issue with pdb and realised that the cached metadata on `django.db.models.managers.Manager` is seemingly incorrect. A lot of times the plugin is writing data to `metadata` on the `django.db.models.managers.Manager` `TypeInfo` during semantic analysis. However, depending on how mypy traverses a project's files cache could be dumped for the `django.db.models.managers` file __before__ the plugin has completed. So some metadata exists in memory but is never written to cache.

One of the things that is (intended to be) written to that metadata object is _custom managers_. When inspecting `metadata` in the cache on `django.db.models.managers.Manager` it's practically missing all my custom managers. See extraction of that cache object below

```
  "metadata": {
    "django": {
      "manager_bases": {
        "django.contrib.admin.models.LogEntryManager": 1,
        "django.contrib.auth.base_user.BaseUserManager": 1,
        "django.contrib.auth.models.GroupManager": 1,
        "django.contrib.auth.models.PermissionManager": 1,
        "django.contrib.auth.models.UserManager": 1,
        "django.contrib.contenttypes.models.ContentTypeManager": 1,
        "django.contrib.sites.models.SiteManager": 1,
        "django.db.models.manager.EmptyManager": 1,
        "django.db.models.manager.Manager": 1,
        "django.db.models.manager.RelatedManager": 1
        # I should have seen my custom manager here...
      }
    }
  },
```

Mypy seems to run semantic analysis differently (if run at all) during cached runs, so my custom managers never end up in `manager_bases` above, and are defaulted to having `Any` as return type.

_I think_ the snippet below should help as some kind of a repro case, not sure. I ran it on a larger project and, as I mentioned earlier, this problem depends on how mypy chooses to traverse the files to analyse.

```python
from django.db import models
from django.db.models.manager import BaseManager


class CustomQuerySet(models.QuerySet["CustomModel"]):
    def custom_method(self) -> dict[str, str]:
        return {}


CustomManager = BaseManager.from_queryset(CustomQuerySet)


class CustomModel(models.Model):
    field = models.CharField(max_length=255)

    objects = CustomManager()
```

## How is that should be

Return types should be the same on cached runs as on non-cached runs.


Discussion:

Resolving patch:
diff --git a/mypy_django_plugin/main.py b/mypy_django_plugin/main.py
--- a/mypy_django_plugin/main.py
+++ b/mypy_django_plugin/main.py
@@ -276,7 +276,7 @@ def get_attribute_hook(self, fullname: str) -> Optional[Callable[[AttributeConte
         if (
             info
             and info.has_base(fullnames.BASE_MANAGER_CLASS_FULLNAME)
-            and class_name in self._get_current_manager_bases()
+            and "from_queryset_manager" in helpers.get_django_metadata(info)
         ):
             return resolve_manager_method
 


Test patch: