Skip to main content

Wagtail Seeder

Development-data seeder for Wagtail 7.x (Python 3.10 and later). It creates representative pages, media, snippets and settings by inspecting installed Wagtail models.

Installation

Install from PyPI:

python -m pip install wagtail-seeder

Add "wagtail_seeder" to INSTALLED_APPS. Configure an explicit environment; the package fails closed when it is omitted or unsupported:

WAGTAIL_SEEDER = {
    "ENVIRONMENT": "dev",
    "HOMEPAGE_MODEL": "pages.HomePage",
    "DEFAULT_SITE_PORT": 80,  # use 8000 for Django's runserver
}

Apply the project's migrations, then inspect and seed:

python manage.py migrate
python manage.py inspect_wagtail_seed --format=json --require-complete
python manage.py seed_wagtail --confirm --require-complete
python manage.py seed_wagtail_pages --page-type pages.ArticlePage

seed_wagtail is destructive: it removes Wagtail Site objects and pages below its technical root. Use an isolated development database. seed_wagtail_pages preserves existing pages and reuses representatives when the configured count is met. On an existing site, selective seeding uses its current sites and hostnames and leaves unrelated snippets, forms and site settings untouched. Missing related objects needed by the selected pages are still generated. During a full reset, nullable protected links into the old page tree are detached and populated again. Required protected links block deletion; the original sites, pages and detached links are restored when deletion fails. Selected page types can also be seeded through Content Builder in the Wagtail admin without resetting the site. Choose an existing subsite first; with a single site, it is selected automatically. Each dashboard run creates one new page per selected type with a distinct generated title and slug. Existing pages keep their content and revisions. Required new ancestors are populated and published too. Placement and generated page links stay within the chosen site's root locale, excluding nested subsites. Availability is checked for that site, subject to Wagtail's placement and count limits. Create a Site first if none exists. The destructive reset is only available through the CLI. The dashboard is available to active users with the explicitly assigned wagtail_seeder.run_seeder permission. Superusers also need a direct or group grant; their automatic Django permission bypass does not apply. Existing users are not granted access on upgrade. CLI commands require the explicit allowed environment and do not impersonate a user.

After migrate, assign Can run Content Builder through Django's user/group administration or Wagtail's group permissions. Users also need normal Wagtail admin access. Removing the grant immediately hides the menu and blocks seed requests.

The dashboard includes a moving loading bar and elapsed time during a seed, searchable page selection, and a searchable, sortable result table with status filters. The loading bar indicates activity rather than a completion percentage. The report starts collapsed at the top of the dashboard and can be removed with Clear report. It is shown once after a seed and disappears when the page is refreshed or left. Each concrete page has one result row with expandable details; other results are grouped separately. Report items include optional page_id, page_title and page_model metadata, an optional translated user_message, plus is_summary (default False). The original message remains available for CLI output and technical details. Summary rows are excluded from outcome counts so a failure is counted once. Report rows offer Open page for live, routable pages and Edit when the current user can edit the page. Frontend links use the page's own site URL. Skipped block variants include a readable location and explanation of the limit; raw block paths and validation errors are available under Technical details. All interface text and user messages use English source strings with Dutch translations. They follow the interface language selected in the Wagtail user profile, falling back to Wagtail's active/default admin language. The dashboard's JavaScript catalog uses the same language for counters, elapsed time and request errors. Translation source and compiled catalogs are included in both packages.

Existing selective CLI calls still reuse representative pages. Opt into the dashboard's new-page behavior and restrict generation to an existing Wagtail Site:

python manage.py seed_wagtail_pages --page-type pages.ArticlePage --site-id 2 --create-new

The Python equivalents are Seeder().seed_page_types(["pages.ArticlePage"], site_id=2, create_new=True) and Seeder().page_seed_plans(site_id=2). Both keyword arguments are optional; without them, selective generation retains its previous behavior.

Sites and languages

A single-site project needs only the configuration above. Use SITE_ROOTS for multiple public sites or languages. Every root creates its own Site; descendants, page links, settings and translatable snippets stay within their applicable scope.

WAGTAIL_SEEDER = {
    "ENVIRONMENT": "dev",
    "DEFAULT_SITE_PORT": 80,
    "SITE_ROOTS": [
        {
            "page_model": "pages.HomePage",
            "hostname": "localhost",
            "language_code": "nl",
            "is_default_site": True,
        },
        {
            "page_model": "pages.HomePage",
            "hostname": "en.localhost",
            "language_code": "en",
        },
    ],
}

language_code must be in Wagtail's content languages. It resolves or creates a Locale; it does not enable translation settings or translate text. Omit it to use the project's default content language. Do not put a language string in field_values["locale"]: that field expects a Locale instance.

Each root requires page_model; optional values include hostname, port, site_name, is_default_site, slug and protected field_values. Give it a unique key when another configured root uses parent_page_key.

SEED_ROOT_PAGES uses the same page configuration without creating another Site. Use it only for technical branches that otherwise cannot be reached. parent_page_model or parent_page_key deliberately selects an explicit parent; force_root: True deliberately overrides creation restrictions at the technical root. An explicit parent and force_root cannot be combined.

Generation and validation

The seeder creates draft page structures before filling dependencies and publishing. Editor presets keep their existing text and structure while empty required fields and nested blocks are filled. Existing valid relationships are preserved, including relationships in explicitly declared many-to-many intermediate models. Scalar fields required by the page editor are filled even when the model permits blank values, without changing the model's field definitions. It uses Wagtail's allowed parent/child models, can_create_at(), global max_count and per-parent limits. Titles come from model names: NewsOverviewPage becomes seed-News Overview, followed by seed-News Overview 2 when that title already exists. Explicitly configured titles are retained. Generated text uses readable page/block and field names, for example seed-Hero-Title, with sample paragraphs for longer text. Unique values only receive an extra suffix when needed, including uniqueness within a locale. Valid defaults and populated content are retained, including visibility flags, locales, translation keys and configured values.

PAGE_COUNTS maps model labels to positive counts per public tree and locale; the default is one of each applicable concrete type. A subclass does not count as its parent type. Wagtail's global singleton limits still take precedence. The dashboard and --create-new add one page per selected type per run instead of filling this representative count. UNIQUE_PAGE_SCOPES adds post-seed checks for combinations of site, region and/or language.

Stream/List blocks and InlinePanels follow their minimum/maximum counts, including nested block_counts. By default, streams contain a compact representative sample. Enable Populate all block types in the dashboard, pass --all-block-types to seed_wagtail or seed_wagtail_pages, or use Seeder(all_block_types=True) to try every available block type, including nested streams and streams inside lists. Existing blocks are retained and missing types are added when space permits. Configured providers and protected field values remain authoritative. Excluded, unsupported or capacity-limited types are reported; mandatory content must still validate. This does not generate a separate block for each dropdown choice.

REQUIRED_STREAMFIELD_ITEM_COUNTS supplies additional business constraints not declared on the block:

"REQUIRED_STREAMFIELD_ITEM_COUNTS": {"pages.ArticlePage.highlights": 2}

SNIPPET_COUNTS accepts model labels or legacy class names (labels take precedence). The default is four; use zero to request no new instances. Translatable snippets are generated for every configured root locale. Existing instances are reused and validated, and stale chooser links are repaired without replacing their text. Site settings are populated before saving, then revisited after their hooks have created any additional pages. Page-owned inlines keep their original parent.

When wagtailstreamforms is installed, the seeder builds local contact forms from registered single-line, email and multiline field blocks and configured templates. It also supports older installations with separate FormField models. Submission hooks stay empty; the seeder does not submit forms or send mail.

The inspector only reads configuration and model structure; it does not execute model validation or prove rendering works. seed_wagtail --require-complete checks both that graph and the results of actual model/block validation. Missing mandatory content fails the command. Optional unsupported blocks/inlines are reported as skipped; declared exclusions are intentional. Failed objects are rolled back, while successful objects remain available for diagnosis.

Small project providers

Use providers for business rules that cannot be inferred from field declarations. Keep them in the consuming project and configure dotted paths:

"MODEL_VALUE_PROVIDERS": ["myproject.seeding.model_values"],
"FIELD_VALUE_PROVIDERS": ["myproject.seeding.field_values"],
"BLOCK_VALUE_PROVIDERS": ["myproject.seeding.block_values"],

Model providers receive (instance, context) during prepare (before generic population) and validate (before validation). Make them idempotent. Field providers receive (instance, field, context) during pre/post, including relation and StreamField fields. Block providers receive (block, context). Field and block providers return wagtail_seeder.providers.UNHANDLED to delegate generation; returning None intentionally supplies an empty value.

Context includes seeder, config, scoped pages, phase, instance/owner, site, locale, a zero-based index, field_name and a tuple block_path. For model providers, index is stable within the model's ordered instances and locale scope. For nested block providers it identifies the current stream/list item. Protected field_values bypass generic field generation and link repair; model providers may deliberately change them.

For example, a project that retrieves exactly one navigation object per language can assign the configured languages using the model provider's index. Match SNIPPET_COUNTS to the number of languages. See the configuration guide for generic examples covering multiple sites, custom validation, technical page types and external dependencies.

External integrations

Exclude models and blocks whose validation or rendering needs an external service:

"EXCLUDED_SNIPPET_MODELS": ["newsletter.MailingList"],
"SKIP_BLOCK_CLASS_NAMES": {"NewsletterBlock": "requires an external service"},

Exclusions also apply to indirect chooser/required-relation creation. Block exclusions match class names, qualified names and subclasses. A required excluded dependency is an error; a provider or a different valid local block is needed. External optional video URL fields with empty valid defaults stay empty. Seeder settings cannot turn off network calls hardcoded in a project's page rendering or save hooks.

Other optional keys are ALLOWED_ENVIRONMENTS (default {"dev"}), ALLOWED_HOSTNAMES (dashboard request guard), FAKER_LOCALE (default "nl_NL"), DEFAULT_SITE_HOSTNAME (default "localhost") and DEFAULT_SITE_PORT (default 8000). Complete local compatibility requires testing the generated frontend and editor in the consuming project; static inspection cannot guarantee arbitrary custom validation or JavaScript behavior.

Development and testing

The repository separates the distributable package from its local test project:

src/wagtail_seeder/    Python package, management commands and admin templates
tests/test_project/   Runnable Django/Wagtail project, local database and media
tests/models.py       Example page and relation models
tests/migrations/     One initial migration for the example models
tests/test_*.py       Automated tests

From the repository root, create an environment and install the package in editable mode. Changes in src then take effect immediately:

python -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
pytest

The default installation uses the latest available Wagtail 7.x. To test the lowest supported release series, use a separate environment and install with python -m pip install -e ".[dev]" "wagtail>=7,<7.1".

Run the same checks as CI:

python tests/test_project/manage.py check
python tests/test_project/manage.py makemigrations --check --dry-run
ruff check .
ruff format --check .
pytest

Automated tests use an isolated database and temporary media directories. They do not change the local demo database or leave generated media in the repository root.

Local Wagtail project

No external project or Bakery Demo checkout is required. From the repository root:

python tests/test_project/manage.py migrate
python tests/test_project/manage.py createsuperuser
python tests/test_project/manage.py inspect_wagtail_seed --require-complete
python tests/test_project/manage.py seed_wagtail --confirm --require-complete
python tests/test_project/manage.py runserver

Open http://127.0.0.1:8000/admin/ and log in. Explicitly assign Can run Content Builder to the account or its group before opening Content Builder in the menu. Visit http://127.0.0.1:8000/admin/content-builder/, choose a subsite and select page types to create new pages. The destructive full seed requires seed_wagtail --confirm through the CLI. Open http://127.0.0.1:8000/ to browse the generated pages.

For example, test the selective command with:

python tests/test_project/manage.py seed_wagtail_pages --page-type tests.ArticlePage

The local database, uploaded/generated media and collected static files live under tests/test_project/ and are ignored by Git. The project is configured for local development only. The example models use one initial migration. If a local demo database was created with the previous migration layout, recreate that demo database before migrating. Installed packages continue to use the separate wagtail_seeder permission migration.

Building and releasing

Build the wheel and source distribution and check their metadata:

python -m build
python -m twine check dist/*
python scripts/check_distribution.py
tar -tf dist/wagtail_seeder-*.tar.gz
unzip -l dist/wagtail_seeder-*.whl

Before publishing, install the built wheel in the target project's environment with python -m pip install /path/to/wagtail_seeder-1.0.0-py3-none-any.whl, then apply migrations and the local Seeder configuration. Check a full seed, selective seeding, frontend rendering and saving generated pages in the editor. This tests the packaged assets and any custom validation in the application.

Runtime code and templates come from src/wagtail_seeder/. Tests, demo databases and media are excluded from both distributions. The source archive also contains the README, license and build metadata.

Public releases are published from GitLab CI to PyPI using Trusted Publishing. Before the first release, configure a pending publisher on PyPI (or a publisher on the existing project) with:

  • PyPI project: wagtail-seeder
  • GitLab namespace: fourdigits/private-packages
  • GitLab project: wagtail-seeder
  • Pipeline file: .gitlab-ci.yml
  • Environment: pypi

Protect the v* tag pattern in GitLab. No stored PyPI API token is needed. Update the version in the project metadata and src/wagtail_seeder/__init__.py, then push a protected tag matching that version, for example v1.0.0. CI requires the tag and both version declarations to agree, and publishes only after tests and package checks pass. Published versions cannot be overwritten; bump the version for each new release.

License

BSD-3-Clause. See LICENSE.

Metadata

Release files for wagtail-seeder 1.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wagtail-seeder 1.1.3
File Size Uploaded Provenance Actions
wagtail_seeder-1.1.3.tar.gz 60.4 kB Without provenance Details for wagtail_seeder-1.1.3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for wagtail-seeder 1.1.3
File Interpreter ABI Platform Actions
wagtail_seeder-1.1.3-py3-none-any.whl Python 3 none any Details for wagtail_seeder-1.1.3-py3-none-any.whl

Total release size: 120.9 kB

Release files / wagtail_seeder-1.1.3.tar.gz

Download URL wagtail_seeder-1.1.3.tar.gz
Size 60.4 kB
Tags Source
SHA-256 checksum
How to use checksums
937be5ea827887969c220ba2f1f2374257f5f5e31a0aa081cd57b38b4fcb4bb6
BLAKE2b-256 checksum
How to use checksums
7225f465f05674a5fa376aae16248ee45e571d25df97f5893fdf72d5c0121c66
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.12.15

Release files / wagtail_seeder-1.1.3-py3-none-any.whl

Download URL wagtail_seeder-1.1.3-py3-none-any.whl
Size 60.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ab28b861e11e8f92f8a992a5bf8436a34ec0c4998920934b0add09ecf6bfac0e
BLAKE2b-256 checksum
How to use checksums
c5b37f96d6c7fb07f0c33246868a0d801e3ee53032f576ab9c08cf499a562d8d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.12.15

Security for wagtail-seeder 1.1.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page