Open atpjpta opened 3 years ago
One more note, I understand the intended use case for factory_boy is for generating test fixtures. I suppose I am somewhat abusing it in the first place, as I am using it to try to fill my development database with plenty of random but semi-realistic data.
I have been doing this for years. I don't think this is an abuse at all.
The major struggle I have faced is that I try to reuse factories for both test fixtures and initial development setup data. But since most of the time factories declarations are tailored for test expressiveness at the expense of data sense, the script loading initial data grows into a somewhat unstructured and coupled spaghetti script.
But I still think it is a successful approach
The function that recalls what you are asking would be the instantiate
method from the options class. You could always override it like
import factory.django
class HackyOptions(factory.django.DjangoOptions):
def instantiate(self, step, args, kwargs):
# ???
obj = super().instantiate(self, step, args, kwargs)
# ???
return obj
class UserFactory(factory.django.DjangoModelFactory):
_options_class = HackyOptions
class Meta:
model = User
# ...
This is not deliberately hidden, and is useful to gain control on complex instance generation details, but I think this is just not expected to be used by regular factory_boy users. factory_boy aims at providing utilities for declarative and composable factories, if you are going the imperative way you might find less trouble by... not using factory_boy!
I believe what you want to achieve can be declaratively done via fakers, sequences (for username uniqueness and whatnot), and traits and "maybe" declarations instead of those functions. It is just an alternative paradigm.
The problem
I found it very hard to randomize models, and even harder to randomize models based on input parameters. I have finally arrived at a solution that basically makes every attribute of my factories a
factory.LazyAttribute
that calls some function to compute random values (using a faker and homebrew mashup). These functions sometimes take nothing, sometimes they take a parameter fromParams
that I can pass in through theLazyAttribute
's input lambda.Here is a snippet of what I've done, for an example:
This is a factory where I wanted to base the user's name off of their gender. I wanted the ability to specify a fake user's genders externally, hence the gender param.
The reason it was so hard to add randomization seems to be the design choice of making all model fields class attributes of the model factory class. Because of this, trying to assign fields to random values right in the class (and as described in the docs) doesn't work. If you do that, they are computed at import time and fixed for the rest of that python session. Using (or abusing?) lazy attribute as I've described above seems to be the only path forward.
Proposed solution
I think that it would be more pythonic, make usage more intuitive, and reduce the learning curve if there was some function of
DjangoModelFactory
and other factory classes that child classes could override, something likebuild_model()
, that is called to construct the model every time one is requested. If fields were then assigned in this function, one could write regular python withrandom
ornp.random
to easily create randomized fields.Extra notes
Could you please comment on the design decision to make all model fields class attributes of the model factory class? I feel like you must have had a good reason for approaching the problem this way, but I can't understand why.
One more note, I understand the intended use case for
factory_boy
is for generating test fixtures. I suppose I am somewhat abusing it in the first place, as I am using it to try to fill my development database with plenty of random but semi-realistic data. I'm an experienced developer, but am very new to web development/django and began usingfactory_boy
for this purpose based on this blog post https://mattsegal.dev/django-factoryboy-dummy-data.html. If you think this is a bad idea, or know a better library that is intended for this purpose, I'd be very grateful for any advice you might have to offer. Thanks!