Атрибут класса модели pytest_django не заполнен для подкласса __init___
У меня есть конкретная проблема.
class A:
SOME_ATTR: int
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
attr = getattr(cls, "SOME_ATTR", None)
if not attr:
raise TypeError(f"Class '{cls.__name__}' must define 'SOME_ATTR'")
classB(models.Model, A):
SOME_ATTR = 0
В этом случае все работает так, как ожидалось, но pytest тестирует. pytest_django создает: class = <class 'поддельный.B'> который не имеет атрибута 'SOME_ATTR' Есть ли какой-нибудь способ обеспечить его соблюдение?
Я пытался добавить функцию автоматического включения области сеанса, но это сработало не так, как раньше.
Когда pytest_django
создает поддельные классы моделей для тестирования, он неправильно переносит class attributes
из parent classes
, что приводит к сбою проверки __init_subclass__
.
Вместо использования __init_subclass__
вы могли бы попробовать использовать метакласс: он перехватывает создание класса на более фундаментальном уровне, чем __init_subclass__
, поэтому проверка происходит непосредственно во время создания класса, и тестовым платформам сложнее ее пропустить!
class AMeta(type):
def __new__(mcs, name, bases, attrs):
cls = super().__new__(mcs, name, bases, attrs)
if name != 'A': # Skip validation for the base class itself
attr = getattr(cls, "SOME_ATTR", None)
if attr is None:
raise TypeError(f"Class '{cls.__name__}' must define 'SOME_ATTR'")
return cls
class A(metaclass=AMeta):
SOME_ATTR: int
class B(models.Model, A):
SOME_ATTR = 0
Как указал @sahasrara62, добавление метода __new__
непосредственно к методу class A
- это тоже один из способов.
class A:
SOME_ATTR: int
def __new__(cls, *args, **kwargs):
attr = getattr(cls, "SOME_ATTR", None)
if attr is None and cls.__module__ != '__fake__':
raise TypeError(f"Class '{cls.__name__}' must define 'SOME_ATTR'")
return super().__new__(cls)
Причина, по которой я бы рекомендовал использовать метаклассы uisng aprroch, заключается в том, что они проверяются во время определения класса, выявляя ошибки сразу после загрузки кода, а не при создании экземпляров объектов. Это обеспечивает более быструю обратную связь во время разработки, поэтому производительность не снижается во время выполнения. Кроме того, в моделях Django уже широко используются метаклассы, что концептуально согласуется с архитектурой Django!