I solved this by simply calling the factories at @factory.post_generation . Strictly speaking, this is not a solution to a specific given problem, but I will explain in detail below why this turned out to be the best architecture. The @rhunwick solution does indeed pass SubFactory(LazyAttribute('')) to RelatedFactory , however the restrictions remained, which meant that it was wrong for my situation.
We move the creation of sub_object and object from ProblemFactory to ObjectWithSubObjectsFactory (and delete the exclude clause) and add the following code to the end of ProblemFactory .
@factory.post_generation def post(self, create, extracted, **kwargs): if not create: return
So, it seems that RelatedFactory calls RelatedFactory made before PostGeneration calls.
Naming in this question is easier to understand, so here is the same solution code for this problem:
Creating dataset , column_1 and column_2 moved to the new factory DatasetAnd2ColumnsFactory , and then the code below is added to the end of the 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())
Then I expanded this approach by passing parameters for factory settings, for example:
whatever = WhateverFactory(options__an_option=True, options__another_option=True)
Then this factory code detected the parameters and generated the required test data (note that the method is renamed to options to match the parameter name prefix):
@factory.post_generation def options(self, create, not_used, **kwargs): # The standard code as above if kwargs.get('an_option', None): # code for custom option 'an_option' if kwargs.get('another_option', None): # code for custom option 'another_option'
Then I expanded it. Since my desired models contained self, my factory is recursive. Therefore, for a call such as:
whatever = WhateverFactory(options__an_option='xyz', options__an_option_for_a_nested_whatever='abc')
Inside @factory.post_generation I have:
class Meta: model = Whatever
Some notes you don't need to read about why I went with this option and not with the correct @rhunwicks solution on my question above. There were two reasons.
The thing that prevented me from experimenting with it was that the order of RelatedFactory and post-generation is not reliable - it seems to be affected by unrelated factors, apparently the result of a lazy assessment. I had errors when many factories suddenly stopped working for no apparent reason. This was once because I renamed the variables that were assigned. This sounds ridiculous, but I tested it to death (and posted it here ), but there is no doubt - renaming the variables reliably switched the sequence of work with ShareFactory and post-gen. I still thought it was some kind of supervision on my behalf until it happened again for some other reason (which I never managed to diagnose).
Secondly, I found the declarative code confusing, inflexible, and difficult to override. Itβs not easy to pass different configurations at the time of creating the instance, so the same factory can be used for different types of test data, that is, I had to repeat the code, object needs to be added to the factory list Meta.exclude sounds trivial, but when you have pages data generating code, it was a reliable error. As a developer, you will have to go to several plants several times to understand the flow of control. The generation code will be distributed between the declarative body until you have exhausted these tricks, and the rest will remain alone generation or very confused. A common example for me is a triad of interdependent models (for example, the structure of categories of parents or children or a set of data / attributes / entities) as a foreign key of another triad of interdependent objects (for example, models, parameter values, etc., referring to the parameter values ββof others models). Some of these types of structures, especially nested ones, quickly become unmanageable.
I understand that in fact this is not in the spirit of factory_boy, but everything in the post-generation solves all these problems. I can pass parameters, so the same single factory serves all my requirements for the test data of the composite model, and the code does not repeat. The creation sequence is easy to see immediately, obvious and completely reliable, and not depending on the confusion of the inheritance and redefinition chains and exposure to some error. The interactions are obvious, so you donβt have to digest everything to add some functionality, and the various areas of funtionality are grouped in post-generation if statements. There is no need to exclude working variables, and you can refer to them throughout the life of the factory code. The unit test code is simplified, because the description of the functions occurs in the parameter names, and not in the factory class names, so you create data with a call of type WhateverFactory(options__create_xyz=True, options__create_abc=True.. rather than WhateverCreateXYZCreateABC..() . This does a good separation of duties is pretty clean for the code.