A versatile test fixtures replacement based on thoughtbot's factory_bot for Ruby
—
Specialized factory classes for popular Python ORMs providing database persistence, get-or-create behavior, and ORM-specific features. Factory Boy seamlessly integrates with Django, SQLAlchemy, MongoEngine, and Mogo.
Factory classes and helpers specifically designed for Django models with built-in ORM features.
class DjangoModelFactory(Factory):
"""
Factory for Django models with ORM integration.
Meta attributes:
- model: Django model class
- django_get_or_create: Fields to use for get_or_create behavior (tuple/list)
- database: Database alias to use (string)
- strategy: Default strategy (inherited from Factory)
"""
@classmethod
def _create(cls, model_class, *args, **kwargs):
"""Uses Django ORM create() or get_or_create() based on Meta options."""
class FileField:
"""
Helper for populating Django FileField instances.
Args:
data (bytes, optional): Raw file content
filename (str, optional): Name for the file
from_path (str, optional): Path to read file content from
from_file (file-like, optional): File object to read from
from_func (callable, optional): Function returning file content
"""
def __init__(self, data=None, filename=None, from_path=None, from_file=None, from_func=None): ...
class ImageField(FileField):
"""
Helper for populating Django ImageField instances.
Args:
width (int): Image width in pixels (default: 100)
height (int): Image height in pixels (default: 100)
color (str): Image color (default: 'blue')
format (str): Image format (default: 'JPEG')
**kwargs: Additional FileField arguments
"""
def __init__(self, width=100, height=100, color='blue', format='JPEG', **kwargs): ...
def mute_signals(*signals):
"""
Context manager/decorator to disable Django signals during factory operations.
Args:
*signals: Django signal objects to mute
Returns:
Context manager that can also be used as decorator
"""from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from factory.django import DjangoModelFactory, FileField, ImageField, mute_signals
# Django model
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField()
avatar = models.ImageField(upload_to='avatars/')
resume = models.FileField(upload_to='resumes/')
# Django factory
class UserFactory(DjangoModelFactory):
class Meta:
model = User
django_get_or_create = ('username',) # Use get_or_create for username
database = 'default' # Optional database alias
username = Sequence(lambda n: f'user{n}')
email = LazyAttribute(lambda obj: f'{obj.username}@example.com')
first_name = Faker('first_name')
last_name = Faker('last_name')
class ProfileFactory(DjangoModelFactory):
class Meta:
model = Profile
user = SubFactory(UserFactory)
bio = Faker('text', max_nb_chars=200)
# File field with custom content
resume = FileField(
filename='resume.pdf',
data=b'%PDF-1.4 fake pdf content',
from_func=lambda: generate_pdf_bytes()
)
# Image field with specifications
avatar = ImageField(
width=200,
height=200,
color='red',
format='PNG'
)
# Usage
user = UserFactory() # Saved to database
profile = ProfileFactory() # Creates user and profile
# Muting signals
with mute_signals(post_save):
# post_save signals won't fire during this block
users = UserFactory.create_batch(10)
# As decorator
@mute_signals(post_save)
def create_test_data():
return UserFactory.create_batch(100)Factory class for SQLAlchemy models with session management and persistence options.
class SQLAlchemyModelFactory(Factory):
"""
Factory for SQLAlchemy models with session management.
Meta attributes:
- model: SQLAlchemy model class
- sqlalchemy_session: SQLAlchemy session to use
- sqlalchemy_session_persistence: Persistence strategy ('commit', 'flush', or None)
Class attributes:
- SESSION_PERSISTENCE_COMMIT = 'commit'
- SESSION_PERSISTENCE_FLUSH = 'flush'
"""
SESSION_PERSISTENCE_COMMIT = 'commit'
SESSION_PERSISTENCE_FLUSH = 'flush'
@classmethod
def _create(cls, model_class, *args, **kwargs):
"""Creates SQLAlchemy instance and manages session persistence."""from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from factory.alchemy import SQLAlchemyModelFactory
# SQLAlchemy setup
Base = declarative_base()
engine = create_engine('sqlite:///test.db')
Session = sessionmaker(bind=engine)
session = Session()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
username = Column(String(50))
email = Column(String(100))
# Factory with session management
class UserFactory(SQLAlchemyModelFactory):
class Meta:
model = User
sqlalchemy_session = session
sqlalchemy_session_persistence = 'commit' # or 'flush' or None
username = Sequence(lambda n: f'user{n}')
email = LazyAttribute(lambda obj: f'{obj.username}@example.com')
# Usage
user = UserFactory() # Created and committed to database
users = UserFactory.create_batch(5) # All committed
# Different persistence strategies
class FlushOnlyUserFactory(SQLAlchemyModelFactory):
class Meta:
model = User
sqlalchemy_session = session
sqlalchemy_session_persistence = SQLAlchemyModelFactory.SESSION_PERSISTENCE_FLUSH
username = Sequence(lambda n: f'flush_user{n}')
# Creates and flushes but doesn't commit
user = FlushOnlyUserFactory()Factory class for MongoEngine documents with automatic saving.
class MongoEngineFactory(Factory):
"""
Factory for MongoEngine documents with automatic persistence.
Meta attributes:
- model: MongoEngine Document class
"""
@classmethod
def _create(cls, model_class, *args, **kwargs):
"""Creates MongoEngine document and calls save() if it's a Document."""from mongoengine import Document, StringField, IntField, connect
from factory.mongoengine import MongoEngineFactory
# Connect to MongoDB
connect('test_db')
# MongoEngine model
class User(Document):
username = StringField(required=True)
email = StringField()
age = IntField()
# MongoEngine factory
class UserFactory(MongoEngineFactory):
class Meta:
model = User
username = Sequence(lambda n: f'user{n}')
email = LazyAttribute(lambda obj: f'{obj.username}@example.com')
age = Faker('random_int', min=18, max=65)
# Usage
user = UserFactory() # Saved to MongoDB
users = UserFactory.create_batch(10) # All saved to MongoDBFactory class for Mogo (pymongo wrapper) objects.
class MogoFactory(Factory):
"""
Factory for Mogo (pymongo wrapper) objects with automatic persistence.
Meta attributes:
- model: Mogo model class
"""
@classmethod
def _create(cls, model_class, *args, **kwargs):
"""Creates Mogo instance and calls save() method."""from mogo import Model, Field
from factory.mogo import MogoFactory
# Mogo model (assuming mogo is configured)
class User(Model):
username = Field()
email = Field()
age = Field()
# Mogo factory
class UserFactory(MogoFactory):
class Meta:
model = User
username = Sequence(lambda n: f'user{n}')
email = LazyAttribute(lambda obj: f'{obj.username}@example.com')
age = Faker('random_int', min=18, max=65)
# Usage
user = UserFactory() # Saved via mogo# Multiple database support (Django)
class PrimaryDBUserFactory(DjangoModelFactory):
class Meta:
model = User
database = 'primary'
class AnalyticsDBUserFactory(DjangoModelFactory):
class Meta:
model = User
database = 'analytics'# Django get_or_create
class CategoryFactory(DjangoModelFactory):
class Meta:
model = Category
django_get_or_create = ('name',) # Won't create duplicates by name
name = Iterator(['Technology', 'Sports', 'News'])
slug = LazyAttribute(lambda obj: slugify(obj.name))
# Always returns existing or creates new based on name
tech_category = CategoryFactory(name='Technology') # Gets existing or creates# SQLAlchemy with custom session handling
from sqlalchemy.orm import scoped_session
class TransactionalUserFactory(SQLAlchemyModelFactory):
class Meta:
model = User
sqlalchemy_session = scoped_session(sessionmaker())
sqlalchemy_session_persistence = None # Manual control
username = Sequence(lambda n: f'user{n}')
@classmethod
def _create(cls, model_class, *args, **kwargs):
instance = super()._create(model_class, *args, **kwargs)
# Custom transaction logic
cls._meta.sqlalchemy_session.add(instance)
return instance# Django with custom file handling
class DocumentFactory(DjangoModelFactory):
class Meta:
model = Document
title = Faker('sentence')
# Generate PDF content dynamically
file = FileField(
filename=LazyAttribute(lambda obj: f'{slugify(obj.title)}.pdf'),
from_func=lambda: generate_pdf_content()
)
# Image with dynamic dimensions
thumbnail = ImageField(
width=LazyFunction(lambda: random.randint(100, 300)),
height=LazyFunction(lambda: random.randint(100, 300)),
format='PNG'
)# Complex signal muting
from django.db.models.signals import post_save, pre_save, post_delete
@mute_signals(post_save, pre_save, post_delete)
class SilentUserFactory(DjangoModelFactory):
class Meta:
model = User
username = Sequence(lambda n: f'silent_user{n}')
# Conditional signal muting
def create_users_silently(count):
with mute_signals(post_save):
return UserFactory.create_batch(count)Install with Tessl CLI
npx tessl i tessl/pypi-factory-boy