2015-10-07 4 views
0

У меня есть номер function с номером parameters, а затем специализированный instantiation этой функции, с некоторыми settings для каждого из параметров функции. Таким образом, у меня есть структура вроде следующего:Получение идентификатора связанных дочерних записей в factory_boy

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)? Или мне нужно посмотреть базу данных?

ответ

-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') 

Ниже демонстрирует решения нескольких других проблем, кто использует эту модель, вероятно, столкнуться, например, перекрывая ценности объектов, связанных с, а также ссылки на другие таблицы, которые сами связаны между собой. Он в значительной степени опирается на методы, которые Xelnor представил в своем ответе.

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)) 

Теперь мне нужно создать набор данных с некоторыми столбцами данных и объединить записи параметров с этими столбцами. Чтобы сделать это, это идет в конце FunctionToParameterSettingsFactory:

@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() 

Это правда, немного Hacky. Я пробовал пропустить column=column_1 в вызовах RelatedFactory, но это вызвало создание нескольких наборов данных, причем каждый столбец был связан с другим.Я пробовал все виды акробатики с помощью SelfAttribute и LazyAttribute, но вы не можете использовать их в вызове RelatedFactory, и вы не можете создать что-то с SubFactory (SelfAttribute()), а затем передать его в функцию «СвязанныйFactory», поскольку это нарушает SelfAttribute (см. my other question).

В моем реальном коде у меня было еще несколько моделей с внешним ключом для набора данных, и все это было хорошо связано.

+0

Мои правки были rej Извините, извините @ Xelnor, я пытался сохранить ответ с вами. – Chris

+0

В последнем примере кода есть ошибка. 'dataset' повторно создается так, что' column_1' и 'column_2' получают разные наборы данных (оба с переопределенным полем' name'). Если я создаю 'column_3' с' dataset = factory.LazyAttribute (lambda col: col.factory_parent.dataset) ', он отлично работает. Если я затем добавлю 'column_3' к вызову' ParameterSettingFactory', я получаю 'AttributeError: набор данных параметров неизвестен. ', А factory_boy находит только локаторы ParameterSettingFactory и no factory_parent. – Chris

+0

Я решил вышеупомянутую проблему [описанную здесь] (http://stackoverflow.com/posts/33110180/edit) и обновил ответ. – Chris

2

factory.SubFactory предназначенный для см. a 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') 

Calling, что завод будет выполните следующие действия:

  1. Создайте объект Function (через FunctionFactory)
  2. Вызов ParameterSettingFactory, направляя его на созданный объект Function
  3. Вызов ParameterSettingFactory во второй раз, по-прежнему указывает на тот же объект Function
  4. Вернись, что функциональный объект.
+0

Извините, несколько секунд назад я обновил свой вопрос с помощью 'RelatedFactory' вместо' SubFactory', пока вы печатали. Просто читал «SelfAttribute», но безнадежно застрял в строках 'function.parameter [0] .id'. Потребует мне несколько минут, чтобы понять, что ваш код так медлит со мной, прежде чем я отмечу как ответ. Огромное спасибо, поскольку вы можете сказать, что я новичок в factory_boy и т. Д. – Chris

+0

Некоторые заметки, само собой разумеющиеся для любого, кто знаком с factory_boy. Синтаксис 'factory.SelfAttribute ('..' говорит SubFactory ('FunctionInstantiationFactory'), чтобы получить именованный объект (' function') из области вызывающей фабрики ('ParameterSettingFactory').' Exclude' останавливает factory_boy от попытки сохранить ' function' в качестве внешнего ключа объекта 'ParameterSetting' (который не имеет этого FK).' FunctionWithParametersFactory', наследуя от 'FunctionFactory', начинается с создания' Function', затем использует 'ParameterSettingFactory' для создания других трех Огромное спасибо @Xelnor, это меня многому научило. – Chris

+0

Из [docs] (http://factoryboy.readthedocs.org/en/latest/reference.html#parents) Я думаю 'function = factory.LazyAttribute (лямбда-функция_параметр: function_parameter.factory_parent.function) 'будет альтернативным (худшим) способом реализации второй' function = factory.SelfAttribute ('.. function') '. @ Решение Xelnor лучше и аккуратнее, просто отметив соответствующее открытие – Chris

Смежные вопросы