Аннотация динамического типа для пользовательского метода менеджеров моделей Django

Мне нужна помощь с подсказкой типа для метода пользовательского менеджера моделей.

Это мой пользовательский менеджер и базовая модель. Я наследую эту базовую модель для других моделей, чтобы не писать общие поля снова и снова.

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

Это пример модели:

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

Теперь об этом:

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

Аннотация типа выглядит следующим образом, когда я навожу курсор в IDE:

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

Но я хочу, чтобы аннотация типа выглядела следующим образом:

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

Как я могу это сделать? Это работает для методов по умолчанию, таких как get и filter. Но как сделать это для пользовательских методов, таких как get_or_none?

Пожалуйста, помогите мне.

Спасибо

Для того чтобы ваша IDE видела get_or_none метод, вы должны добавить аннотацию типа к objects переменной в вашем BaseModel классе, как например:

objects: BaseManager = BaseManager()

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.

Тогда выглядит так: [GitHub]:

from typing_extensions import Self


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

Если вы включите поддержку Django в PyCharm, PyCharm обычно устанавливает django-stubs за шторками, чтобы помочь вам с этими проверками типов.

Таким образом, они переписали тип Manager, чтобы убедиться, что его можно подписать типом модели, с которой работает менеджер.

Вы можете добавить файл .pyi, в котором содержится подсказка:

# 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: …

и убедитесь, что _T содержит ссылку на модель с:

# 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]]

Обычно PyCharm подбирает подсказки типов.

Вернуться на верх