Неправильное поведение при наследовании на фабриках фабричных мальчиков

У меня есть несколько заводов с такой структурой:

AbstractFactoryMinimal(DjangoModelFactory):
    comment = ''

AbstractFactoryFull(AbstractFactoryMinimal):
    comment = Faker(provider='text', max_nb_chars=2000)

FactoryMinimal(AbstractFactoryMinimal):
    field = ''

    class Meta(object):
        model = SomeModel

FactoryFull(FactoryMinimal, AbstractFactoryFull):
    field = Faker(provider='text', max_nb_chars=2000)

obj = FactoryFull()
print(obj.comment) # expect some text from faker, but got '' instead
print(obj.field)   # works like expected, return some random text

В моделях у меня

AbstractModel(TimeStampedModel)

а также

SomeModel(AbstractModel)

Я даже смотрю на mro, и он выглядит именно так, как я ожидал:

(FactoryFull,
FactoryMinimal,
AbstractFactoryFull,
AbstractFactoryMinimal,
factory.django.DjangoModelFactory,
factory.base.Factory,
factory.base.BaseFactory,
object)

Таким образом, поле «комментарий» должно быть сгенерировано мошенником, а не просто установлено с помощью «». Почему так работает? Как я могу реализовать фабрики, чтобы увидеть ожидаемое поведение?

РЕДАКТИРОВАТЬ: я могу изменить порядок наследования на последней фабрике на это:

FactoryFull(AbstractFactoryFull, FactoryMinimal)

это будет работать, но тогда, если я захочу переопределить поле в FactoryMinimal, это не сработает. И иногда мне нужна именно эта функция. И вообще, это не нормальное поведение наследования в Python.


person Dmitriy Repin    schedule 21.07.2017    source источник


Ответы (2)


Поэтому я нашел обходной путь.

По сути, я меняю порядок наследования и повторяю некоторый код. Как это:

FactoryMinimal(AbstractFactoryMinimal):
    field = ''

    class Meta(object):
        model = SomeModel

FactoryFull(AbstractFactoryFull, FactoryMinimal):
    field = Faker(provider='text', max_nb_chars=2000)

    class Meta(object):
        model = SomeModel

И если мне нужно переопределить что-то из базового класса, я использую для этого класс MixIn:

CommentFieldMixIn(DjangoModelFactory):
    comment = 'Something Completely Different'

FactoryMinimal(AbstractFactoryMinimal):
    field = ''

    class Meta(object):
        model = SomeModel

FactoryFull(CommentFieldMixIn, AbstractFactoryFull, FactoryMinimal):
    field = Faker(provider='text', max_nb_chars=2000)

    class Meta(object):
        model = SomeModel

Остается только одна проблема: как я могу сказать любому, кто будет поддерживать мой код, почему я делаю это на каждой фабрике.

person Dmitriy Repin    schedule 24.07.2017

# Scan the inheritance chain, starting from the furthest point,
# excluding the current class, to retrieve all declarations.
for parent in reversed(self.factory.__mro__[1:]):
    ...

Причину вы можете увидеть выше. Также проверьте тест в этой фиксации https://github.com/FactoryBoy/factory_boy/commit/e2ef7c96ed74b35b9dec75a7f222b6ffa9214c10 Я сомневаюсь, почему так. Может быть, спросить автора этой библиотеки о мотивации?

person Sergei Zherevchuk    schedule 21.07.2017
comment
Это здорово знать, но как я могу тогда добиться желаемого поведения? - person Dmitriy Repin; 21.07.2017
comment
Поскольку вы не можете полагаться на MRO без изменения библиотеки, избегайте использования множественного наследования таким образом (и мб вообще, кроме миксинов). Это мое единственное предложение. - person Sergei Zherevchuk; 21.07.2017