django-stubs icon indicating copy to clipboard operation
django-stubs copied to clipboard

Mypy can't get custom manager type

Open maximsht opened this issue 3 years ago • 12 comments

I have custom user model that inherits from UserProfile from django-tenant-users package. UserProfile has custom model manager. In my model I see an error from mypy: Mypy: Could not resolve manager type for "Flow_BE.applications.users.models.User.objects" and when i try to use this manager I receive error: Mypy: "Manager[User]" has no attribute "create_user"

This is how models specified:

class User(UserProfile):
    """This User model replaces default user model."""

# from django-tenant-users
class UserProfile(AbstractBaseUser, PermissionsMixinFacade):

    objects = UserProfileManager()

class UserProfileManager(BaseUserManager):

    def create_user(
            self,
            email=None,
            password=None,
            is_staff=False,
            **extra_fields,
        ):...

For User.objects.create_user(...) mypy generates errors. Code works.

System information

  • OS: MacOs 12.3.1
  • python version: 3.10.4
  • django version: 4.0.6
  • mypy version: 0.971
  • django-stubs version: 1.12.0
  • django-stubs-ext version: 0.5.0

maximsht avatar Jul 21 '22 05:07 maximsht

Hi @MaximShteygervald 🙂 Nice to see you again!

We are working on better manager types in a new release, so it might be already fixed. Can you please try master version?

sobolevn avatar Jul 21 '22 07:07 sobolevn

I think this is related to #1023 and the fact that django-tenant-users is an untyped package.

flaeppe avatar Aug 07 '22 08:08 flaeppe

I'm trying to update django-stubs and ext, but I'm being blocked by this issue.

We usually create a custom queryset and use as_manager. To avoid complications we use an explicit (albeit not 100% accurate, but really useful) annotation:

class AttachmentQS(QuerySet['Attachment']):
     def load(self, pk) -> 'Attachment': ...

class Attachment(Model):
     # ...
     objects: AttachmenQS = AttachmentQS.as_manager()  # type: ignore 

This has worked pretty well so far, but trying to update apparently makes our hack being ignored and I get lots of issues like:

kaiko/attachments/tasks.py:112:18: error: "Manager[Attachment]" has no attribute "load"

when calling Attachment.objects.load(...). Does django-stubs-ext overrides the explicit type hint somehow? (Is it even possible to override it)?

mvaled avatar Aug 16 '22 09:08 mvaled

@mvaled See

  • https://github.com/typeddjango/django-stubs#my-queryset-methods-are-returning-any-rather-than-my-model, and
  • https://github.com/typeddjango/django-stubs/pull/1025

flaeppe avatar Aug 16 '22 09:08 flaeppe

Hi @flaeppe,

Notice that issue is different in two regards:

  • They comment the type of instances becomes Any,
  • which allows any attribute/method in the instances.

In this case is the actual manager's type the one missing the methods in the queryset.

The line that fails is calling Attachment.objects.<some_custom_method>; but, in any case, since I put an explicit type hint, the type of Attachment.objects should not be Manager[Attachment] but the type I explicitly attached to it.

Just to test, I did the change to use from_queryset:

class AttachmentQS(QuerySet['Attachment']):
     def load(self, pk) -> 'Attachment': ...

AttachmentManager = Manager.from_queryset(AttachmentQS)

class Attachment(Model):
     # ...
     objects = AttachmentManager()

And still the same error is reported.

image

mvaled avatar Aug 16 '22 09:08 mvaled

[...] since I put an explicit type hint, the type of Attachment.objects should not be Manager[Attachment] but the type I explicitly attached to it.

I fear this is overwritten by the plugin code (at least when using .from_queryset(...))

Just to test, I did the change to use from_queryset

If you got that error without an explicit annotation, there's probably a bug somewhere.

flaeppe avatar Aug 16 '22 09:08 flaeppe

Oh! I've just realized that I did a slightly different thing:

objects = Manager.from_queryset(AttachmentQS)()

Which issues this warning:

kaiko/attachments/models.py:132:5: error: Could not resolve manager type for "kaiko.attachments.models.Attachment.objects"
kaiko/attachments/models.py:132:15: error: `.from_queryset` called from inside model class body

Extracting the manager, does solve this particular issue.

mvaled avatar Aug 16 '22 09:08 mvaled

I had the very same issue with django-safedelete. My solution was a PR against upstream https://github.com/makinacorpus/django-safedelete/pull/213 . Thankfully it got merged quickly.

sebastian-philipp avatar Aug 17 '22 11:08 sebastian-philipp

In my opinion Manager generic should accept Model, Quesryset[Model] types allowing to specify any class that extends Quesryset[Model] as the queryset

agalazis avatar Sep 03 '22 11:09 agalazis

@mvaled See

* https://github.com/typeddjango/django-stubs#my-queryset-methods-are-returning-any-rather-than-my-model, and

* [Implement support for `<QuerySet>.as_manager()` #1025](https://github.com/typeddjango/django-stubs/pull/1025)

What is the first link supposed to communicate? The answer to the question is just "if you're doing this [example] or [this]" but there's no consequent: if you're doing this things … then what?

The PR in the second link has been merged, but I still get errors: first, a 'need type annotation for "objects"' error when doing query_set.as_manager() or Manager.from_queryset(query_set)(), and second, revealed types of Any when using the querysets.

bwo avatar Nov 16 '23 16:11 bwo

What is the first link supposed to communicate? The answer to the question is just "if you're doing this [example] or [this]" but there's no consequent: if you're doing this things … then what?

I think the revision behind the link said something different than today, at the time the comment was written. Before support for <QuerySet>.as_manager() existed. But you're totally right that section doesn't really provide any answer right now, it should probably be removed.

The PR in the second link has been merged, but I still get errors: first, a 'need type annotation for "objects"' error when doing query_set.as_manager() or Manager.from_queryset(query_set)(), and second, revealed types of Any when using the querysets.

It's really hard to say anything without more context. Feel free to post an example, repro case or any other context that you think could be useful to help hunt down the cause of the issue you're seeing.

flaeppe avatar Nov 16 '23 18:11 flaeppe

It's really hard to say anything without more context. Feel free to post an example, repro case or any other context that you think could be useful to help hunt down the cause of the issue you're seeing.

Yeah, I eventually worked out how to do what I wanted as I attempted to repro and discovered that the actual case depends on the fact that the model in question is a PostgresPartitionedModel from django-postgres-extra—which is typed but doesn't seem to play nicely with the plugin. Would be nice, but I assume that's a big feature.

bwo avatar Nov 16 '23 20:11 bwo