Skip to content

Instantly share code, notes, and snippets.

@emorozov
Forked from mgaitan/autofactory.py
Last active June 24, 2025 11:30
Show Gist options
  • Save emorozov/c2b243985166c135708f20e2d752e1c6 to your computer and use it in GitHub Desktop.
Save emorozov/c2b243985166c135708f20e2d752e1c6 to your computer and use it in GitHub Desktop.
Automatically define factory boy recipes for dataclasses by inspecting the type annotations
## See https://github.com/FactoryBoy/factory_boy/issues/836
import inspect
from typing import Literal, get_args, get_origin
import factory
import factory.fuzzy
from dataclasses import Field, MISSING, is_dataclass
from enum import Enum
from datetime import date, datetime
def get_auto_field(field: Field):
if field.default is not MISSING:
return field.default
elif field.type is str and 'email' in field.name:
return factory.Faker("ascii_email")
elif field.type is datetime:
return factory.Faker("date_time_between")
elif field.type is date:
return factory.Faker("date_object")
elif is_dataclass(field.type):
# fixme reflect the factory un a better way than eval
return factory.SubFactory(eval(f'{field.type.__name__}AutoFactory'))
elif inspect.isclass(field.type) and issubclass(field.type, Enum):
return factory.fuzzy.FuzzyChoice(field.type.__members__.values())
elif get_origin(field.type) in [list, tuple, set]:
args = get_args(field.type)
return factory.Faker(f'py{get_origin(field.type).__name__}', value_types=args)
elif get_origin(field.type) is Literal:
args = get_args(field.type)
return factory.fuzzy.FuzzyChoice(args)
# str, int, float, decimal
return factory.Faker(f'py{field.type.__name__.lower()}')
def auto_factory(target_model, field_overrides=None):
factory_name = f'{target_model.__name__}AutoFactory'
class Meta:
model = target_model
attrs = {name: get_auto_field(field) for name, field in target_model.__dataclass_fields__.items()}
attrs.update(field_overrides or {})
attrs['Meta'] = Meta
return type(factory_name, (factory.Factory,), attrs)
@emorozov
Copy link
Author

Adds support for Literal fields.

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