Skip to content

sqlalchemy-seedlingAsync-native seeder & factory library

Built for SQLAlchemy 2.0+ async workflows — seed databases, generate fixtures, and run factories without the boilerplate.

sqlalchemy-seedling

How it compares ​

For an async SQLAlchemy 2.0 app that needs both fixture factories and a seeder pipeline, the alternatives are usually factory_boy plus a hand-rolled script. Here's the same job, three ways.

Seed 1 000 rows + run a dependent seeder ​

python
class UserFactory(AutoFactory[User]):
    model = User


class UserSeeder(Seeder):
    environments = DEV_AND_TEST

    async def run(self, session):
        await UserFactory.create_batch(session, 1000, bulk=True)


class ProfileSeeder(Seeder):
    depends_on = [UserSeeder]
    environments = DEV_AND_TEST

    async def run(self, session): ...


runner = SeederRunner(session_factory, env="development")
runner.register(UserSeeder, ProfileSeeder)
await runner.run()
python
class UserFactory(SQLAlchemyModelFactory):
    class Meta:
        model = User
        sqlalchemy_session_persistence = "flush"

    name = factory.Faker("name")
    email = factory.Faker("email")
    # ...every column declared manually


# factory_boy is sync — bridge to async yourself
def seed_users(sync_session):
    UserFactory._meta.sqlalchemy_session = sync_session
    UserFactory.create_batch(1000)


def seed_profiles(sync_session): ...


# Ordering, environments, idempotency, parallel — DIY
seed_users(session)
seed_profiles(session)

Feature matrix ​

Seedlingfactory_boyraw SQLAlchemy
Async-native API✅❌ (sync only)✅
Zero-config field defaults from mapper✅ AutoFactory❌ declare each❌
Bulk INSERT … RETURNING path✅ bulk=True❌✅ manual
Seeder orchestration (depends_on)✅❌❌
Environment gating (dev / test / prod)✅❌❌
State tracking + drift detection✅❌❌
Parallel level execution✅❌❌
pytest fixtures + @seed() decorator✅❌❌
CLI (seed run, fresh, graph, …)✅❌❌
Per-row throughput (1 000 rows, SQLite)7 900 rows/s16 700 rows/s8 200 rows/s
Bulk throughput (1 000 rows, SQLite)90 000 rows/s—520 000 rows/s

Honest trade-off: factory_boy is still ~2× faster per-row because its sync code path has no event-loop overhead. Seedling's per-row path now matches raw session.add() + flush, and bulk=True is ~5× faster than factory_boy. See the benchmarks page for full numbers.