Проблемы с созданием протокола типов для пользовательского класса Django
У меня есть функция, которая оперирует пользователями, но в одном из проектов, где она используется, мы не используем Django user. У нас есть класс данных, который притворяется пользователем.
Проблемы начались, когда я пытался напечатать эту функцию. Я не мог использовать AbstractBaseUser в качестве типа пользователя, потому что это явно не работало бы в этом проекте с dataclass user.
Однако в Python есть решение для таких проблем - протоколы!
Я быстро создал пользовательский протокол, содержащий все возможности, которые я использовал в функции:
class ManagerProtocol(Protocol):
def get(self, **kwargs: Any) -> UserProtocol:
...
class UserProtocol(Protocol):
objects: ManagerProtocol
class DoesNotExist(Exception):
...
и настроили нашего пользователя класса данных, чтобы он следовал этому протоколу:
class FakeManager:
def get(self, **kwargs: Any) -> StaticUser:
return StaticUser(user_id=kwargs.get('user_id', ''))
@dataclass(frozen=True)
class StaticUser:
user_id: str
is_authenticated: bool = True
objects = FakeManager()
class DoesNotExist(Exception):
pass
и затем я запустил mypy и получил следующее:
error: Argument 1 to "function" has incompatible type "StaticUser"; expected "UserProtocol" [arg-type]
note: Following member(s) of "StaticUser" have conflicts:
note: DoesNotExist: expected "Type[UserProtocol.DoesNotExist]", got "Type[StaticUser.DoesNotExist]"
note: objects: expected "ManagerProtocol", got "FakeManager"
Я прочитал несколько статей в Интернете, и решение было ясным - использовать свойства вместо членов класса!
class UserProtocol(Protocol):
@property
def objects(self) -> ManagerProtocol:
...
@property
def DoesNotExist(self) -> Type[Exception]:
...
но после повторного запуска mypy возникли другие проблемы:
error: "Callable[[UserProtocol], ManagerProtocol]" has no attribute "get" [attr-defined]
error: Exception type must be derived from BaseException [misc]
Совместимость протоколов была в порядке, но mypy видел ошибки в тех местах, где я действительно использовал класс UserProtocol
.
У вас есть идеи, как создать протокол, который бы работал здесь, не вызывая ошибок типа?