在factory_boy中获取关联子记录的id

时间:2015-10-07 14:37:04

标签: django python-3.x factory-boy associated-object

我有一个function,其数量为parameters,然后是该函数的专用instantiation,每个函数的参数都有一些settings。所以我有如下结构:

class Function(models.Model):
    name = models.CharField()

class FunctionParameter(models.Model):
    function = models.ForeignKey(Function)

class FunctionInstantiation(models.Model):
    function = models.ForeignKey(Function)

class ParameterSetting(models.Model):
    function_instantiation = models.ForeignKey(FunctionInstantiation)
    function_parameter = models.ForeignKey(FunctionParameter)

FunctionFactory我可以使用factory.RelatedFactory创建parameters

但在FunctionInstantiationFactory我无法使用factory.RelatedFactory(ParameterSetting)创建ParameterSettings,因为我无法访问parameter内创建的FunctionFactory个对象,所以我无法设置parameter_setting.function_parameter_id

FunctionInstantiationFactory如何查找parameter_id中创建的FunctionFactory个参数?我可以从RelatedFactory(FunctionFactory)的返回值获得它们吗?或者我需要查看数据库吗?

2 个答案:

答案 0 :(得分:2)

factory.SubFactory旨在关注 ForeignKey;如果您想以相反的方式使用它,则应使用RelatedFactory代替。

就你的例子而言,我会选择以下工厂:

class FunctionFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Function
    name = factory.Sequence(lambda n: "Function %d" % n)


class FunctionParameterFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.FunctionParameter
    function = factory.SubFactory(FunctionFactory)


class FunctionInstantiationFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.FunctionInstantiation
    function = factory.SubFactory(FunctionFactory)


class ParameterSettingFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.ParameterSetting
        exclude = ['function']

    # We'll need a FunctionFactory; this field is part of 'exclude',
    # thus available while building the factory but not passed to the
    # target Django model
    function = factory.SubFactory(FunctionFactory)

    # Use the function from our Factory for both
    # function_instantiation and function_parameter
    function_instantiation = factory.SubFactory(FunctionInstantiationFactory,
        function=factory.SelfAttribute('..function'))
    function_parameter = factory.SubFactory(FunctionParameterFactory,
        function=factory.SelfAttribute('..function'))

您可以添加额外的工厂FunctionWithParametersFactory,以创建参数:

class FunctionWithParametersFactory(FunctionFactory):
    parameter1 = factory.RelatedFactory(ParameterSettingFactory, 'function')
    parameter2 = factory.RelatedFactory(ParameterSettingFactory, 'function')

调用该工厂将执行以下操作:

  1. 创建一个Function对象(通过FunctionFactory)
  2. 调用ParameterSettingFactory,将其指向创建的Function对象
  3. 第二次调用ParameterSettingFactory,仍将其指向同一个Function对象
  4. 返回该Function对象。

答案 1 :(得分:-1)

这是Xelnor的答案,但修复了错误,因此只创建了一个function_instantiation,而不是每个parameter / parameter_setting对创建一个class FunctionFactory(factory.django.DjangoModelFactory): class Meta: model = models.Function name = factory.Sequence(lambda n: "Function %d" % n) class FunctionParameterFactory(factory.django.DjangoModelFactory): class Meta: model = models.FunctionParameter function = factory.SubFactory(FunctionFactory) class FunctionInstantiationFactory(factory.django.DjangoModelFactory): class Meta: model = models.FunctionInstantiation function = factory.SubFactory(FunctionFactory) class ParameterSettingFactory(factory.django.DjangoModelFactory): class Meta: model = models.ParameterSetting function_instantiation = factory.SubFactory(FunctionInstantiationFactory) function_parameter = factory.SubFactory(FunctionParameterFactory, function=factory.SelfAttribute('..function_instantiation.function')) class FunctionToParameterSettingsFactory(FunctionInstantiationFactory): class Meta: model = models.FunctionInstantiation # This overrides the function_instantiation created inside # ParameterSettingFactory, which then overrides the Function creation, # with the SelfAttribute('..function_instantiation.function') syntax. parameter_setting_1 = factory.RelatedFactory(ParameterSettingFactory, 'function_instantiation') parameter_setting_2 = factory.RelatedFactory(ParameterSettingFactory, 'function_instantiation')

class FunctionFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Function
    name = factory.Sequence(lambda n: "Function %d" % n)


class FunctionParameterFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.FunctionParameter
    name = factory.Sequence(lambda n: "Function %d" % n)
    function = factory.SubFactory(FunctionFactory)


class ParameterSettingFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.ParameterSetting
    name = factory.Sequence(lambda n: "Function %d" % n)

    function_instantiation = factory.SubFactory(FunctionInstantiationFactory)
    function_parameter = factory.SubFactory(FunctionParameterFactory,
        function=factory.SelfAttribute('..function_instantiation.function'))


class DatasetAnd2ColumnsFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.Function
    dataset = factory.SubFactory(DatasetFactory,
        name=factory.Sequence(lambda n: "Custom dataset %d" % n))
    column_1 = factory.SubFactory(ColumnFactory, dataset=dataset,
        name=factory.Sequence(lambda n: "Column 1 %d" % n))
    column_2 = factory.SubFactory(ColumnFactory, dataset=dataset,
        name=factory.Sequence(lambda n: "Column 2 %d" % n))


# I found it neater not to inherit in the end, due to needing quite a lot of
# additional complexity not included in my original question.
class FunctionToParameterSettingsFactory(factory.django.DjangoModelFactory):
    class Meta:
        model = models.FunctionInstantiation

    name = factory.Sequence(lambda n: "Custom instantiation name %d" % n)
    # You can call Sequence to pass values to SubFactories
    function = factory.SubFactory(FunctionFactory, 
        name=factory.Sequence(lambda n: "Custom function %d" % n))

    parameter_setting_1 = factory.RelatedFactory(ParameterSettingFactory, 
        'function_instantiation',
        # Note the __ syntax for override values for nested objects:
        parameter__name='Parameter 1',
        name='Parameter Setting 1')
    # Possible to use Sequence here too, and makes looking at data easier
    parameter_setting_2 = factory.RelatedFactory(ParameterSettingFactory, 
        'function_instantiation',
        parameter__name=factory.Sequence(lambda n: "Param 1 for fn %d" % n),
        name=factory.Sequence(lambda n: "Param Setting 1 for fn %d" % n))

以下演示了使用此模式的任何人可能遇到的其他一些问题的解决方案,例如覆盖相关对象的值,以及链接到其他表本身的链接。它主要来自Xelnor在答案中介绍的技术。

FunctionToParameterSettingsFactory

我现在需要创建一个包含一些数据列的数据集,并将parameter_setting记录与这些列连接起来。为此,这将在@factory.post_generation def post(self, create, extracted, **kwargs): if not create: return dataset = DatasetAnd2ColumnsFactory() column_ids_by_name = dict((column.name, column.id) for column in dataset.column_set.all()) # self is the `FunctioInstantiation` Django object just created by the `FunctionToParameterSettingsFactory` for parameter_setting in self.parametersetting_set.all(): if parameter_setting.name == 'age_in': parameter_setting.column_id = column_ids_by_name['Age'] parameter_setting.save() elif parameter_setting.name == 'income_in': parameter_setting.column_id = column_ids_by_name['Income'] parameter_setting.save()

结尾处进行
column=column_1

这无疑是有点hacky。我尝试在RelatedFactory调用中传递CodeToTest,但这触发了多个数据集的创建,每个列链接到不同的数据集。我尝试过使用SelfAttribute和LazyAttribute的各种杂技,但是你不能在RelatedFactory调用中使用它们,也不能用SubFactory(SelfAttribute())创建一些东西,然后将它传递给RelatedFactory,因为它会破坏SelfAttribute(参见my other question)。

在我的真实代码中,我有几个带有数据集外键的模型,它们都很好。