Skip to content

Instantly share code, notes, and snippets.

@tobiasmcnulty
Last active October 27, 2020 15:12
Show Gist options
  • Save tobiasmcnulty/6232216 to your computer and use it in GitHub Desktop.
Save tobiasmcnulty/6232216 to your computer and use it in GitHub Desktop.
Django management command to generate factory-boy boilerplate factory definitions for all the models in a given app. The code is not intended to be usable right off the bat, it just provides a few sane defaults based on the structure of your models.
from django.core.management.base import BaseCommand, CommandError
from django.db.models import get_models, get_app, fields
from django.db.models.fields import related
class Command(BaseCommand):
help = """Generate factory-boy factories for the given app"""
def handle(self, *args, **options):
assert len(args) == 1, 'Must specify app name as first and only argument'
app = get_app(args[0])
models = get_models(app)
app_label = models[0]._meta.app_label
print """
import factory
import datetime
import {models} as {app_label}_models
# import other requried factories here
""".format(models=app.__name__, app_label=app_label)
needed_factories = set()
created_factories = set()
for model in models:
print """
class {model_name}Factory(factory.django.DjangoModelFactory):
FACTORY_FOR = {app_label}_models.{model_name}
""".format(app_label=app_label, model_name=model.__name__)
for field in model._meta.fields:
# skip fields with default values or that can be blank
if isinstance(field, fields.AutoField):
continue
if field.default != fields.NOT_PROVIDED:
print ' # skipped field {0} ({1}) with default value ({2})'.format(field.name, field.__class__.__name__, field.default)
continue
if field.blank or field.null:
print ' # skipped blank or nullable field {0} ({1})'.format(field.name, field.__class__.__name__)
continue
args = []
model_slug = model._meta.verbose_name.replace(' ', '-')
if field.choices:
cls = 'factory.fuzzy.FuzzyChoice'
args.append('** need choices **')
elif isinstance(field, fields.IntegerField):
cls = 'factory.fuzzy.FuzzyInteger'
args.append('0')
elif isinstance(field, fields.EmailField):
cls = 'factory.Sequence'
args.append('lambda n: "{0}-{1}-{{}}@example.com".format(n)'.format(model_slug, field.name))
elif isinstance(field, fields.CharField) or isinstance(field, fields.TextField):
cls = 'factory.Sequence'
args.append('lambda n: "{0}-{1}-{{}}".format(n)'.format(model_slug, field.name))
elif isinstance(field, fields.DateTimeField):
cls = 'factory.fuzzy.FuzzyNaiveDateTime'
args.append('datetime.datetime.now() - datetime.timedelta(days=365)')
elif isinstance(field, fields.DateField):
cls = 'factory.fuzzy.FuzzyDate'
args.append('datetime.date.today() - datetime.timedelta(days=365)')
elif isinstance(field, related.ForeignKey) or isinstance(field, related.OneToOneField):
cls = 'factory.SubFactory'
rel = field.rel.to.__name__
rel_factory = '{0}Factory'.format(rel)
needed_factories.add(rel_factory)
args.append(rel_factory)
else:
cls = 'factory.UNKNOWN'
args.append(field.__class__.__name__)
#import pdb
#pdb.set_trace()
print ' {name} = {cls}({args})'.format(name=field.name, cls=cls, args=', '.join(args))
created_factories.add('{model_name}Factory'.format(model_name=model.__name__))
print '' # two lines between class definitions
print '# TODO: you also need to create and add imports for the '\
'following related factories: {0}'.format(', '.join(needed_factories - created_factories))
@vkurup
Copy link

vkurup commented Aug 7, 2015

Google brought me here! Updated to Python 3 (just making print a function) and it still works: https://gist.github.com/vkurup/7b5d4d37de7ce82c42e8

Thanks Tobias!

@hminnovation
Copy link

Thanks Tobias! Google also brought me here, was a super helpful gist to get factory boy setup on a project the way I wanted.

@Ruslan-Skira
Copy link

Tobias thank you ) I have been importing my class with factories and they are run two times((. And use name == main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment