admin管理员组

文章数量:1125077

I need help with type hint for a custom model manager method.

This is my custom manager and a base model. I inherit this base model to other models so that I don't have to write common fields again and again.

class BaseManager(models.Manager):
    def get_or_none(self, *args, **kwargs):
        try:
            return self.get(*args, **kwargs)
        except self.model.DoesNotExist:
            return None

class BaseModel(models.Model):
    id = models.UUIDField(
        primary_key=True, default=uuid.uuid4, verbose_name="ID", editable=False
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = BaseManager()  # Set the custom manager

    class Meta:
        abstract = True

This is an example model:

class MyModel(BaseModel):
    category = models.CharField(max_length=10)

Now for this:

my_object = MyModel.objects.get_or_none(
            category="...",
        )

The type annotation is like this when I hover in my IDE:

my_object: BaseModel | None = MyModel.objects. get_or_none(...

But I want the type annotation like this:

my_object: MyModel | None = MyModel.objects. get_or_none(...

How can I do that? This works for the default methods like get and filter. But how to do this for custom methods like get_or_none?

Please help me.

Thanks

I need help with type hint for a custom model manager method.

This is my custom manager and a base model. I inherit this base model to other models so that I don't have to write common fields again and again.

class BaseManager(models.Manager):
    def get_or_none(self, *args, **kwargs):
        try:
            return self.get(*args, **kwargs)
        except self.model.DoesNotExist:
            return None

class BaseModel(models.Model):
    id = models.UUIDField(
        primary_key=True, default=uuid.uuid4, verbose_name="ID", editable=False
    )
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = BaseManager()  # Set the custom manager

    class Meta:
        abstract = True

This is an example model:

class MyModel(BaseModel):
    category = models.CharField(max_length=10)

Now for this:

my_object = MyModel.objects.get_or_none(
            category="...",
        )

The type annotation is like this when I hover in my IDE:

my_object: BaseModel | None = MyModel.objects. get_or_none(...

But I want the type annotation like this:

my_object: MyModel | None = MyModel.objects. get_or_none(...

How can I do that? This works for the default methods like get and filter. But how to do this for custom methods like get_or_none?

Please help me.

Thanks

Share Improve this question asked 2 days ago Nahidujjaman HridoyNahidujjaman Hridoy 2,2073 gold badges17 silver badges35 bronze badges 2
  • Do you use django-stubs or some other typing library, because by default Python's type system could not interfere that for .get either. – willeM_ Van Onsem Commented 2 days ago
  • No, I didn't explicitly install django-stubs. Is there any guide I can follow? Or can you please answer on this question if you can help? – Nahidujjaman Hridoy Commented 2 days ago
Add a comment  | 

2 Answers 2

Reset to default 0

Python's type inference system can not interfere this, because the code works a bit too much with meta-programming, etc. and the interpreter can not "get through" that (yet). For example a package like django-stubs [GitHub] contains type hints that help the type checker to understand the types.

This then looks like [GitHub]:

from typing_extensions import Self


class Model(metaclass=ModelBase):
    # …
    objects: ClassVar[Manager[Self]]

If you enable Django support in PyCharm, PyCharm will normally install django-stubs behind the curtains to help you with these type checks.

They thus rewrote the Manager type to make sure one can subscript it with the type of the model the manager deals with.

You can add a .pyi file that drops the hint with:

# path/to/base/manager.pyi

from typing import Generic, Tuple, TypeVar

from django.db.models.base import Model
from django.db.models.manager import Manager

_T = TypeVar('_T', bound=Model, covariant=True)


class BaseManager(Generic[_T], Manager[_T]):
    def get_or_none(self, *args, **kwargs) -> _T | None: …

and ensure that _T contains a reference to the model with:

# path/to/base/model.pyi

from typing import ClassVar
from django.db.models.base import Model
from path.to.base.manager import BaseManager
from typing_extensions import Self

class BaseModel(Model):
    objects: ClassVar[OrderHistoryManager[Self]]

Normally PyCharm will then pick up the type hints.

In order for your IDE to see get_or_none method, You should add type annotation to objects variable in your BaseModel class like so:

objects: BaseManager = BaseManager()

本文标签: pythonDynamic type annotation for Django model managers custom methodStack Overflow