Last active
October 27, 2020 15:12
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Tobias thank you ) I have been importing my class with factories and they are run two times((. And use name == main