-
-
Save vkurup/7b5d4d37de7ce82c42e8 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 | |
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): | |
class Meta: | |
model = {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__) | |
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