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
- 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. - 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:loaddatafails when a field the fixture omits is now required. - Uniform randomness.
fakerproduces plausible values, not production distributions. If your realstatuscolumn 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:
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:
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.
| Goal | Command |
|---|---|
| Load fixture rows into the database | python manage.py loaddata |
| Export current rows as a fixture | python manage.py dumpdata |
| Apply migrations before seeding | python manage.py migrate |
| Clear every table's rows | python manage.py flush |
| Generate a production-shaped dataset with no fixtures | npx --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.
Related guides
- PostgreSQL data generator — what a Postgres data generator does, and how it relates to fake and dummy data
- Postgres test data — the five ways to get test data, compared
- Postgres seed data generator — the tool comparison
- Postgres synthetic data — synthetic vs. mock vs. anonymized
- Rails seed data and Laravel seed data — the same loop in other stacks
- Quickstart — first seed in minutes