factory_boy извлекает оригинальные kwargs

Я создаю фабрику с factory_boy, которая генерирует модель django. Я хотел бы видеть, какие аргументы пользователь вводит встроенными. Моя фабрика выглядит так

class SomeFactory(factory.django.DjangoModelFactory):

    name = factory.Sequence(lambda n: 'Instance #{}'.format(n))
    some_other_thing = factory.SubFactory(SomeOtherFactory)

    class Meta:
        model = SomeModel

Теперь пользователь может сказать s = SomeFactory(), и это будет работать нормально, но я хочу определить, вводит ли пользователь свой собственный аргумент. Например, чтобы узнать, передал ли пользователь свое имя, как в s = SomeFactory(name='Matt')

То, что я пробовал до сих пор,

  • Writing my own __init__ function in the SomeFactory class
    • This gets mysteriously overwritten and is neither called when I call s = SomeFactory(), nor when I call s.__init__()
  • То же самое касается перезаписи метода __new__.
  • Overwriting the poorly named _adjust_kwargs
    • This gives me all fields as kwargs, not just the ones the user defined. For instance, calling s = SomeFactory(name='Matt'), I would get a kwargs dict with keys for name and some_other_thing, which makes it impossible to tell input their own argument or not
  • Overwriting _create
    • Still encounter the same problem with overwriting _adjust_kwargs, in that kwargs doesn't contain the original kwargs, but rather all of the arguments

Я думаю, что многие функции, которые мне нужны, заключены в черный ящик внутри factory_boy StepBuilder (я подозреваю, что это в методе instantiate), но я понятия не имею, как изменить его, чтобы он делал то, что я хочу.

У кого-нибудь есть мысли о том, как выяснить, какие kwargs были заданы изначально при вызове s = SomeFactory()? т.е. определить, что если я сказал s = SomeFactory(name='Matt'), что пользователь вручную установил имя?

Спасибо!

Обновление: я использую django версию 1.11.2, factory_boy версию 2.8.1 и python версию 3.5.2.


person mjkaufer    schedule 14.06.2017    source источник


Ответы (1)


Вы можете переопределить метод create, чтобы получать только пользовательские kwargs.

Полный пример будет примерно таким:

from django.contrib.auth.models import User
import factory


class UserFactory(factory.DjangoModelFactory):
    username = factory.Sequence(
        lambda n: 'test'
    )
    email = factory.Sequence(lambda n: 'user{0}@example.com'.format(n))

    class Meta:
        model = User

    @classmethod
    def create(cls, **kwargs):

        # here you'll only have the kwargs that were entered manually

        print(str(kwargs))

        return super(UserFactory, cls).create(**kwargs)

Итак, когда я называю это:

In [2]: UserFactory(username='foobar')
{'username': 'foobar'}
Out[2]: <User: foobar>

Если вы хотите поймать kwargs для других стратегий сборки, кроме create, вам также нужно будет сделать это для методов stub и build.

person dan_kaufhold    schedule 19.06.2017
comment
Привет, @dan_kaufhold, к сожалению, метод _prepare не работает для моей проблемы. Метод запускается, когда я создаю новую фабрику, но значение kwargs не равно исходным переданным аргументам. Он содержит все аргументы — например, в вашем примере вызов UserFactory(password='apple') даст мне kwargs из {"email": "[email protected]", "password": "test123"}. Я пытаюсь определить, было ли обязательное поле введено пользователем вручную или создано с помощью фабричного мальчика. В вашем примере я не могу определить, был ли email создан пользователем или машиной. - person mjkaufer; 19.06.2017
comment
@mjkaufer Присмотритесь. Пароль устанавливается только в test123, если в kwargs уже нет другого пароля. Когда вы на самом деле проверите, что такое kwargs в первом примере кода, где я прокомментировал, вы увидите, что это именно то kwargs, которое вы вставили. В примере с UserFactory я делаю именно это. Я проверяю, указан ли пароль в kwargs, и манипулирую им. - person dan_kaufhold; 20.06.2017
comment
Но в моем коде я хочу увидеть, переопределяется ли существующее поле. В вашем примере, если пароль был полем в UserFactory, вы не смогли бы различить, был ли пароль kwarg введен вручную или компьютер сгенерировал его на основе метода _prepare. - person mjkaufer; 20.06.2017
comment
@mjkaufer Я понимаю, что ты имеешь в виду. Обновил мой ответ. Метод __call__ включает только введенные вами kwargs, а не те, которые объявлены полями. - person dan_kaufhold; 20.06.2017
comment
Я пробовал __call__ некоторое время назад, но безрезультатно - метод даже не запускался. Какую версию factory_boy/django вы используете? У меня django версия 1.11.2 и factory_boy версия 2.8.1 - person mjkaufer; 20.06.2017
comment
Использование одних и тех же версий. Метод __call__ всегда вызывается FactoryMetaClass, чтобы определить, выполнять ли стратегию сборки, создания или заглушки. Итак, поскольку это, кажется, очень центральная часть фабрики, я бы предположил, что ее всегда нужно вызывать, независимо от того, какую фабрику вы создаете. Простой пример выше определенно работает в новом проекте Django. - person dan_kaufhold; 21.06.2017
comment
Вы пытались запустить свой пример, потому что он определенно не работает с моей стороны - person mjkaufer; 21.06.2017
comment
Конечно, вывод и код напрямую скопированы из моего теста. - person dan_kaufhold; 21.06.2017
comment
Это действительно странно, __call__ не срабатывает с моей стороны - какую версию python вы используете, я использую 3.5.2 - person mjkaufer; 21.06.2017
comment
Хорошо, понятия не имею, почему это не срабатывает, давайте переместим его на один шаг выше в стеке вызовов и перейдем к методу класса create. Я обновил пример. - person dan_kaufhold; 21.06.2017
comment
Красиво, работает отлично. Спасибо за всю твою помощь! - person mjkaufer; 21.06.2017