Django seed data

PostgreSQL test data for Django projects — beyond fixtures and factory_boy, with FK integrity by construction.

Django projects seed through python manage.py loaddata fixtures or a custom management command that creates model instances — often with factory_boy and Faker. Both work, and both duplicate your schema in code that drifts from it. This guide shows the Django-native seeding workflow, where it breaks, and the schema-driven alternative.

How Django seeding normally works

Django owns the schema through migrations (python manage.py migrate). Seeding is either fixtures or a management command:

# users/management/commands/seed.py
from django.core.management.base import BaseCommand
from faker import Faker
from users.models import User
 
 
class Command(BaseCommand):
    def handle(self, *args, **options):
        fake = Faker()
        for _ in range(50):
            User.objects.create(
                email=fake.email(),
                name=fake.name(),
            )

Works for a standalone model. The moment Order has a ForeignKey to User, the command has to create the user, keep its primary key, and pass it to the order — and fixtures have to hard-code those keys.

Where Django seed scripts break

  1. Foreign keys. Order.objects.create(user=...) needs a saved parent. The command grows ordering logic, and fixtures need literal primary keys that break whenever the sequence changes.
  2. Schema drift. A new non-null field, a migration that renames a column, a CheckConstraint — each one is a seed change. Fixtures are especially brittle: loaddata fails when a field the fixture omits is now required.
  3. Uniform randomness. faker produces plausible values, not production distributions. If your real status column is 70% active, a flat loop gives you roughly a third of each — and query plans in tests stop matching production.

Schema-driven seeding with Weavori

Weavori reads the database schema — the one Django migrated — and generates rows from it. The Django-specific workflow:

1. Migrate as usual
$ python manage.py migrate
2. Seed from the schema
$ npx --yes @weavori/cli generate postgres://localhost:5432/mydb --rows 1000

Weavori introspects tables, foreign keys, types, and constraints, resolves dependency order (parents before children), and writes via COPY. Every generated order.user_id references a real generated user — by construction, not by the command's ordering logic.

The schema is the seed

When a Django migration changes the schema, the generated data changes with it. There is no management command or fixture file to update because nothing duplicated the schema.

Django + Weavori in CI

The loop that keeps test databases honest:

$ python manage.py migrate
$ npx --yes @weavori/cli generate $TEST_DATABASE_URL --rows 1000 --output plain

Weavori supports API keys for headless auth, standardized exit codes, and offline license validation — CI runners on restricted networks don't phone home. Full GitHub Actions / GitLab / CircleCI / Jenkins examples in the CI/CD guide.

Which Django seed command do you need?

Django ships no db:seed. Projects either load fixtures or write a management command (python manage.py seed) that builds model instances in code — and both re-encode schema knowledge Django already has in its models and migrations.

GoalCommand
Load fixture rows into the databasepython manage.py loaddata
Export current rows as a fixturepython manage.py dumpdata
Apply migrations before seedingpython manage.py migrate
Clear every table's rowspython manage.py flush
Generate a production-shaped dataset with no fixturesnpx --yes @weavori/cli generate postgres://localhost:5432/mydb

python manage.py loaddata

loaddata reads a fixture file — JSON, XML, or YAML — and inserts the rows it describes. It is the closest thing Django has to a seed command, and its weakness is that fixtures hard-code primary keys: an Order row pointing at "user": 1 is only valid while the sequence behind that id behaves the way it did when the fixture was written. loaddata also fails outright when a field the fixture omits has since become required.

python manage.py dumpdata

dumpdata is where those fixtures come from: it serializes the current contents of the database to a fixture file. That makes it a reasonable way to snapshot a hand-built development database, and the wrong way to keep test data current — the file is a photograph of one schema version, and every migration afterwards drifts it further.

python manage.py migrate

migrate applies pending migrations, and seeding always runs after it: generated rows have to satisfy the columns and constraints the migration just created. In CI, migrate followed by a seed step is the whole loop.

python manage.py flush

flush deletes every row from every table while leaving the schema in place — the "start clean" half of a re-seed. Django pairs it with loaddata or a custom management command. Weavori does not need it: pointed at the database, it fills the tables in FK dependency order with nothing to flush first.

When to keep your Django seed

Honesty about boundaries: keep a seed command or fixture for deterministic unit-test fixtures — a known user the test asserts against. Keep Faker for single values inside tests. Weavori replaces the dataset problem: filling dev, staging, and CI databases with production-shaped, FK-intact rows.

Ready to generate your first dataset?

Install Weavori in one command and connect to any PostgreSQL database.

$npm install -g @weavori/cli
macOS · Linux · Windows/No dependencies required

No credit card required. Start with a 14-day free trial of Pro, then free tier or subscribe.