Атрибут класса модели 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!

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