wagtail-seeder 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wagtail_seeder-1.0.0/LICENSE +28 -0
- wagtail_seeder-1.0.0/MANIFEST.in +10 -0
- wagtail_seeder-1.0.0/PKG-INFO +334 -0
- wagtail_seeder-1.0.0/README.md +309 -0
- wagtail_seeder-1.0.0/docs/compatibility.md +166 -0
- wagtail_seeder-1.0.0/pyproject.toml +49 -0
- wagtail_seeder-1.0.0/setup.cfg +4 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/__init__.py +3 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/apps.py +7 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/config.py +56 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/engine.py +1665 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/guards.py +62 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/management/__init__.py +0 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/management/commands/__init__.py +0 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/management/commands/inspect_wagtail_seed.py +54 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/management/commands/seed_wagtail.py +38 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/management/commands/seed_wagtail_pages.py +35 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/migrations/0001_initial.py +27 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/migrations/__init__.py +0 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/models.py +10 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/providers.py +32 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/report.py +23 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/static/wagtail_seeder/dashboard.css +153 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/static/wagtail_seeder/dashboard.js +183 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/templates/wagtail_seeder/dashboard.html +100 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/values.py +545 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder/wagtail_hooks.py +105 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder.egg-info/PKG-INFO +334 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder.egg-info/SOURCES.txt +30 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder.egg-info/dependency_links.txt +1 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder.egg-info/requires.txt +11 -0
- wagtail_seeder-1.0.0/src/wagtail_seeder.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Four Digits
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
recursive-include src/wagtail_seeder/templates *.html
|
|
2
|
+
recursive-include src/wagtail_seeder/static *.css *.js
|
|
3
|
+
include README.md LICENSE
|
|
4
|
+
include docs/compatibility.md
|
|
5
|
+
prune tests
|
|
6
|
+
prune documents
|
|
7
|
+
prune original_images
|
|
8
|
+
prune build
|
|
9
|
+
prune dist
|
|
10
|
+
global-exclude __pycache__ *.py[cod] *.sqlite3
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wagtail-seeder
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Safe development-data seeding for Wagtail projects.
|
|
5
|
+
Author: Four Digits
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Classifier: Framework :: Django
|
|
8
|
+
Classifier: Framework :: Wagtail
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: >=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: wagtail<8,>=7
|
|
15
|
+
Requires-Dist: Faker>=24
|
|
16
|
+
Requires-Dist: Pillow>=10
|
|
17
|
+
Provides-Extra: dev
|
|
18
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
19
|
+
Requires-Dist: pytest-django>=4.8; extra == "dev"
|
|
20
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == "dev"
|
|
21
|
+
Requires-Dist: ruff>=0.8; extra == "dev"
|
|
22
|
+
Requires-Dist: build>=1; extra == "dev"
|
|
23
|
+
Requires-Dist: twine>=6.1; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# Wagtail Seeder
|
|
27
|
+
|
|
28
|
+
Development-data seeder for Wagtail 7.x (Python 3.10 and later). It creates representative
|
|
29
|
+
pages, media, snippets and settings by inspecting installed Wagtail models.
|
|
30
|
+
|
|
31
|
+
## Installation
|
|
32
|
+
|
|
33
|
+
Install from PyPI:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python -m pip install wagtail-seeder
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Add `"wagtail_seeder"` to `INSTALLED_APPS`. Configure
|
|
40
|
+
an explicit environment; the package fails closed when it is omitted or unsupported:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
WAGTAIL_SEEDER = {
|
|
44
|
+
"ENVIRONMENT": "dev",
|
|
45
|
+
"HOMEPAGE_MODEL": "pages.HomePage",
|
|
46
|
+
"DEFAULT_SITE_PORT": 80, # use 8000 for Django's runserver
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Apply the project's migrations, then inspect and seed:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
python manage.py migrate
|
|
54
|
+
python manage.py inspect_wagtail_seed --format=json --require-complete
|
|
55
|
+
python manage.py seed_wagtail --confirm --require-complete
|
|
56
|
+
python manage.py seed_wagtail_pages --page-type pages.ArticlePage
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`seed_wagtail` is destructive: it removes Wagtail `Site` objects and pages below
|
|
60
|
+
its technical root. Use an isolated development database. `seed_wagtail_pages`
|
|
61
|
+
preserves existing pages and reuses representatives when the configured count is met.
|
|
62
|
+
During a full reset, nullable protected links into the old page tree are detached
|
|
63
|
+
and populated again. Required protected links block deletion; the original sites,
|
|
64
|
+
pages and detached links are restored when deletion fails.
|
|
65
|
+
The same actions are available through **Seeder** in the Wagtail admin to active
|
|
66
|
+
users with the explicitly assigned `wagtail_seeder.run_seeder` permission.
|
|
67
|
+
Superusers also need a direct or group grant; their automatic Django permission
|
|
68
|
+
bypass does not apply. Existing users are not granted access on upgrade.
|
|
69
|
+
CLI commands require the explicit allowed environment and do not impersonate a user.
|
|
70
|
+
|
|
71
|
+
After `migrate`, assign **Can run Wagtail Seeder** through Django's user/group
|
|
72
|
+
administration or Wagtail's group permissions. Users also need normal Wagtail
|
|
73
|
+
admin access. Removing the grant immediately hides the menu and blocks seed requests.
|
|
74
|
+
|
|
75
|
+
The dashboard includes a moving loading bar and elapsed time during a seed,
|
|
76
|
+
searchable page selection, and a searchable, sortable result table with status
|
|
77
|
+
filters. The loading bar indicates activity rather than a completion percentage.
|
|
78
|
+
The last report remains available after refreshing until the next seed. Report
|
|
79
|
+
items include `is_summary` (default `False`); summary rows are excluded from
|
|
80
|
+
outcome counts so a failure is counted once.
|
|
81
|
+
|
|
82
|
+
## Sites and languages
|
|
83
|
+
|
|
84
|
+
A single-site project needs only the configuration above. Use `SITE_ROOTS` for
|
|
85
|
+
multiple public sites or languages. Every root creates its own `Site`; descendants,
|
|
86
|
+
page links, settings and translatable snippets stay within their applicable scope.
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
WAGTAIL_SEEDER = {
|
|
90
|
+
"ENVIRONMENT": "dev",
|
|
91
|
+
"DEFAULT_SITE_PORT": 80,
|
|
92
|
+
"SITE_ROOTS": [
|
|
93
|
+
{
|
|
94
|
+
"page_model": "pages.HomePage",
|
|
95
|
+
"hostname": "localhost",
|
|
96
|
+
"language_code": "nl",
|
|
97
|
+
"is_default_site": True,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"page_model": "pages.HomePage",
|
|
101
|
+
"hostname": "en.localhost",
|
|
102
|
+
"language_code": "en",
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`language_code` must be in Wagtail's content languages. It resolves or creates a
|
|
109
|
+
`Locale`; it does not enable translation settings or translate text. Omit it to use
|
|
110
|
+
the project's default content language. Do not put a language string in
|
|
111
|
+
`field_values["locale"]`: that field expects a `Locale` instance.
|
|
112
|
+
|
|
113
|
+
Each root requires `page_model`; optional values include `hostname`, `port`,
|
|
114
|
+
`site_name`, `is_default_site`, `slug` and protected `field_values`. Give it a unique
|
|
115
|
+
`key` when another configured root uses `parent_page_key`.
|
|
116
|
+
|
|
117
|
+
`SEED_ROOT_PAGES` uses the same page configuration without creating another Site.
|
|
118
|
+
Use it only for technical branches that otherwise cannot be reached.
|
|
119
|
+
`parent_page_model` or `parent_page_key` deliberately selects an explicit parent;
|
|
120
|
+
`force_root: True` deliberately overrides creation restrictions at the technical
|
|
121
|
+
root. An explicit parent and `force_root` cannot be combined.
|
|
122
|
+
|
|
123
|
+
## Generation and validation
|
|
124
|
+
|
|
125
|
+
The seeder creates draft page structures before filling dependencies and publishing.
|
|
126
|
+
Editor presets keep their existing text and structure while empty required fields
|
|
127
|
+
and nested blocks are filled. Existing valid relationships are preserved, including
|
|
128
|
+
relationships in explicitly declared many-to-many intermediate models.
|
|
129
|
+
Scalar fields required by the page editor are filled even when the model permits
|
|
130
|
+
blank values, without changing the model's field definitions.
|
|
131
|
+
It uses Wagtail's allowed parent/child models, `can_create_at()`, global `max_count`
|
|
132
|
+
and per-parent limits. Titles come from model names: `NewsOverviewPage` becomes
|
|
133
|
+
**News Overview**. Valid defaults and populated content are retained, including
|
|
134
|
+
visibility flags, locales, translation keys and configured values.
|
|
135
|
+
|
|
136
|
+
`PAGE_COUNTS` maps model labels to positive counts **per public tree and locale**;
|
|
137
|
+
the default is one of each applicable concrete type. A subclass does not count as
|
|
138
|
+
its parent type. Wagtail's global singleton limits still take precedence.
|
|
139
|
+
`UNIQUE_PAGE_SCOPES` adds post-seed checks for combinations of `site`, `region`
|
|
140
|
+
and/or `language`.
|
|
141
|
+
|
|
142
|
+
Stream/List blocks and InlinePanels follow their minimum/maximum counts, including
|
|
143
|
+
nested `block_counts`. `REQUIRED_STREAMFIELD_ITEM_COUNTS` supplies additional
|
|
144
|
+
business constraints not declared on the block:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
"REQUIRED_STREAMFIELD_ITEM_COUNTS": {"pages.ArticlePage.highlights": 2}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`SNIPPET_COUNTS` accepts model labels or legacy class names (labels take precedence).
|
|
151
|
+
The default is four; use zero to request no new instances. Translatable snippets
|
|
152
|
+
are generated for every configured root locale. Existing instances are reused and
|
|
153
|
+
validated, and stale chooser links are repaired without replacing their text.
|
|
154
|
+
Site settings are populated before saving, then revisited after their hooks have
|
|
155
|
+
created any additional pages. Page-owned inlines keep their original parent.
|
|
156
|
+
|
|
157
|
+
When `wagtailstreamforms` is installed, the seeder builds local contact forms from
|
|
158
|
+
registered single-line, email and multiline field blocks and configured templates.
|
|
159
|
+
It also supports older installations with separate FormField models. Submission
|
|
160
|
+
hooks stay empty; the seeder does not submit forms or send mail.
|
|
161
|
+
|
|
162
|
+
The inspector only reads configuration and model structure; it does not execute
|
|
163
|
+
model validation or prove rendering works. `seed_wagtail --require-complete` checks
|
|
164
|
+
both that graph and the results of actual model/block validation. Missing mandatory
|
|
165
|
+
content fails the command. Optional unsupported blocks/inlines are reported as
|
|
166
|
+
skipped; declared exclusions are intentional. Failed objects are rolled back,
|
|
167
|
+
while successful objects remain available for diagnosis.
|
|
168
|
+
|
|
169
|
+
## Small project providers
|
|
170
|
+
|
|
171
|
+
Use providers for business rules that cannot be inferred from field declarations.
|
|
172
|
+
Keep them in the consuming project and configure dotted paths:
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
"MODEL_VALUE_PROVIDERS": ["myproject.seeding.model_values"],
|
|
176
|
+
"FIELD_VALUE_PROVIDERS": ["myproject.seeding.field_values"],
|
|
177
|
+
"BLOCK_VALUE_PROVIDERS": ["myproject.seeding.block_values"],
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Model providers receive `(instance, context)` during `prepare` (before generic
|
|
181
|
+
population) and `validate` (before validation). Make them idempotent. Field providers
|
|
182
|
+
receive `(instance, field, context)` during `pre`/`post`, including relation and
|
|
183
|
+
StreamField fields. Block providers receive `(block, context)`. Field and block
|
|
184
|
+
providers return `wagtail_seeder.providers.UNHANDLED` to delegate generation;
|
|
185
|
+
returning `None` intentionally supplies an empty value.
|
|
186
|
+
|
|
187
|
+
Context includes `seeder`, `config`, scoped `pages`, `phase`, `instance`/`owner`,
|
|
188
|
+
`site`, `locale`, a zero-based `index`, `field_name` and a tuple `block_path`.
|
|
189
|
+
For model providers, `index` is stable within the model's ordered instances and
|
|
190
|
+
locale scope. For nested block providers it identifies the current stream/list item.
|
|
191
|
+
Protected `field_values` bypass generic field generation and link repair; model
|
|
192
|
+
providers may deliberately change them.
|
|
193
|
+
|
|
194
|
+
For example, a project that retrieves exactly one navigation object per language
|
|
195
|
+
can assign the configured languages using the model provider's index. Match
|
|
196
|
+
`SNIPPET_COUNTS` to the number of languages. See the
|
|
197
|
+
[configuration guide](docs/compatibility.md) for generic examples covering multiple
|
|
198
|
+
sites, custom validation, technical page types and external dependencies.
|
|
199
|
+
|
|
200
|
+
## External integrations
|
|
201
|
+
|
|
202
|
+
Exclude models and blocks whose validation or rendering needs an external service:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
"EXCLUDED_SNIPPET_MODELS": ["newsletter.MailingList"],
|
|
206
|
+
"SKIP_BLOCK_CLASS_NAMES": {"NewsletterBlock": "requires an external service"},
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Exclusions also apply to indirect chooser/required-relation creation. Block exclusions
|
|
210
|
+
match class names, qualified names and subclasses. A required excluded dependency
|
|
211
|
+
is an error; a provider or a different valid local block is needed. External optional
|
|
212
|
+
video URL fields with empty valid defaults stay empty. Seeder settings cannot turn
|
|
213
|
+
off network calls hardcoded in a project's page rendering or save hooks.
|
|
214
|
+
|
|
215
|
+
Other optional keys are `ALLOWED_ENVIRONMENTS` (default `{"dev"}`),
|
|
216
|
+
`ALLOWED_HOSTNAMES` (dashboard request guard), `FAKER_LOCALE` (default `"nl_NL"`),
|
|
217
|
+
`DEFAULT_SITE_HOSTNAME` (default `"localhost"`) and `DEFAULT_SITE_PORT`
|
|
218
|
+
(default `8000`). Complete local compatibility requires testing the generated
|
|
219
|
+
frontend and editor in the consuming project; static inspection cannot guarantee
|
|
220
|
+
arbitrary custom validation or JavaScript behavior.
|
|
221
|
+
|
|
222
|
+
## Development and testing
|
|
223
|
+
|
|
224
|
+
The repository separates the distributable package from its local test project:
|
|
225
|
+
|
|
226
|
+
```text
|
|
227
|
+
src/wagtail_seeder/ Python package, management commands and admin templates
|
|
228
|
+
tests/test_project/ Runnable Django/Wagtail project, local database and media
|
|
229
|
+
tests/models.py Example page and relation models
|
|
230
|
+
tests/migrations/ One initial migration for the example models
|
|
231
|
+
tests/test_*.py Automated tests
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
From the repository root, create an environment and install the package in editable
|
|
235
|
+
mode. Changes in `src` then take effect immediately:
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
python -m venv .venv
|
|
239
|
+
source .venv/bin/activate
|
|
240
|
+
python -m pip install -e ".[dev]"
|
|
241
|
+
pytest
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The default installation uses the latest available Wagtail 7.x. To test the lowest
|
|
245
|
+
supported release series, use a separate environment and install with
|
|
246
|
+
`python -m pip install -e ".[dev]" "wagtail>=7,<7.1"`.
|
|
247
|
+
|
|
248
|
+
Run the same checks as CI:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
python tests/test_project/manage.py check
|
|
252
|
+
python tests/test_project/manage.py makemigrations --check --dry-run
|
|
253
|
+
ruff check .
|
|
254
|
+
ruff format --check .
|
|
255
|
+
pytest
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Automated tests use an isolated database and temporary media directories. They do
|
|
259
|
+
not change the local demo database or leave generated media in the repository root.
|
|
260
|
+
|
|
261
|
+
## Local Wagtail project
|
|
262
|
+
|
|
263
|
+
No external project or Bakery Demo checkout is required. From the repository root:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
python tests/test_project/manage.py migrate
|
|
267
|
+
python tests/test_project/manage.py createsuperuser
|
|
268
|
+
python tests/test_project/manage.py inspect_wagtail_seed --require-complete
|
|
269
|
+
python tests/test_project/manage.py seed_wagtail --confirm --require-complete
|
|
270
|
+
python tests/test_project/manage.py runserver
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
Open `http://127.0.0.1:8000/admin/` and log in. Explicitly assign **Can run Wagtail
|
|
274
|
+
Seeder** to the account or its group before opening **Seeder** in the menu.
|
|
275
|
+
Visit `http://127.0.0.1:8000/admin/seeder/` to test a full seed or
|
|
276
|
+
selected page types. The confirmation checkbox is required for a destructive full
|
|
277
|
+
seed. Open `http://127.0.0.1:8000/` to browse the generated pages.
|
|
278
|
+
|
|
279
|
+
For example, test the selective command with:
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
python tests/test_project/manage.py seed_wagtail_pages --page-type tests.ArticlePage
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
The local database, uploaded/generated media and collected static files live under
|
|
286
|
+
`tests/test_project/` and are ignored by Git. The project is configured for local
|
|
287
|
+
development only.
|
|
288
|
+
The example models use one initial migration. If a local demo database was created
|
|
289
|
+
with the previous migration layout, recreate that demo database before migrating.
|
|
290
|
+
Installed packages continue to use the separate `wagtail_seeder` permission migration.
|
|
291
|
+
|
|
292
|
+
## Building and releasing
|
|
293
|
+
|
|
294
|
+
Build the wheel and source distribution and check their metadata:
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
python -m build
|
|
298
|
+
python -m twine check dist/*
|
|
299
|
+
python scripts/check_distribution.py
|
|
300
|
+
tar -tf dist/wagtail_seeder-*.tar.gz
|
|
301
|
+
unzip -l dist/wagtail_seeder-*.whl
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Before publishing, install the built wheel in the target project's environment
|
|
305
|
+
with `python -m pip install /path/to/wagtail_seeder-1.0.0-py3-none-any.whl`, then
|
|
306
|
+
apply migrations and the local Seeder configuration. Check a full seed, selective
|
|
307
|
+
seeding, frontend rendering and saving generated pages in the editor. This tests
|
|
308
|
+
the packaged assets and any custom validation in the application.
|
|
309
|
+
|
|
310
|
+
Runtime code and templates come from `src/wagtail_seeder/`. Tests, demo databases
|
|
311
|
+
and media are excluded from both distributions. The source archive also contains
|
|
312
|
+
the README, license and build metadata.
|
|
313
|
+
|
|
314
|
+
Public releases are published from GitLab CI to PyPI using
|
|
315
|
+
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/using-a-publisher/).
|
|
316
|
+
Before the first release, configure a pending publisher on PyPI (or a publisher
|
|
317
|
+
on the existing project) with:
|
|
318
|
+
|
|
319
|
+
- PyPI project: `wagtail-seeder`
|
|
320
|
+
- GitLab namespace: `fourdigits/private-packages`
|
|
321
|
+
- GitLab project: `wagtail-seeder`
|
|
322
|
+
- Pipeline file: `.gitlab-ci.yml`
|
|
323
|
+
- Environment: `pypi`
|
|
324
|
+
|
|
325
|
+
Protect the `v*` tag pattern in GitLab. No stored PyPI API token is needed.
|
|
326
|
+
Update the version in the project metadata and
|
|
327
|
+
`src/wagtail_seeder/__init__.py`, then push a protected tag matching that version,
|
|
328
|
+
for example `v1.0.0`. CI requires the tag and both version declarations to agree,
|
|
329
|
+
and publishes only after tests and package checks pass. Published versions cannot
|
|
330
|
+
be overwritten; bump the version for each new release.
|
|
331
|
+
|
|
332
|
+
## License
|
|
333
|
+
|
|
334
|
+
BSD-3-Clause. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# Wagtail Seeder
|
|
2
|
+
|
|
3
|
+
Development-data seeder for Wagtail 7.x (Python 3.10 and later). It creates representative
|
|
4
|
+
pages, media, snippets and settings by inspecting installed Wagtail models.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Install from PyPI:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
python -m pip install wagtail-seeder
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Add `"wagtail_seeder"` to `INSTALLED_APPS`. Configure
|
|
15
|
+
an explicit environment; the package fails closed when it is omitted or unsupported:
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
WAGTAIL_SEEDER = {
|
|
19
|
+
"ENVIRONMENT": "dev",
|
|
20
|
+
"HOMEPAGE_MODEL": "pages.HomePage",
|
|
21
|
+
"DEFAULT_SITE_PORT": 80, # use 8000 for Django's runserver
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Apply the project's migrations, then inspect and seed:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
python manage.py migrate
|
|
29
|
+
python manage.py inspect_wagtail_seed --format=json --require-complete
|
|
30
|
+
python manage.py seed_wagtail --confirm --require-complete
|
|
31
|
+
python manage.py seed_wagtail_pages --page-type pages.ArticlePage
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`seed_wagtail` is destructive: it removes Wagtail `Site` objects and pages below
|
|
35
|
+
its technical root. Use an isolated development database. `seed_wagtail_pages`
|
|
36
|
+
preserves existing pages and reuses representatives when the configured count is met.
|
|
37
|
+
During a full reset, nullable protected links into the old page tree are detached
|
|
38
|
+
and populated again. Required protected links block deletion; the original sites,
|
|
39
|
+
pages and detached links are restored when deletion fails.
|
|
40
|
+
The same actions are available through **Seeder** in the Wagtail admin to active
|
|
41
|
+
users with the explicitly assigned `wagtail_seeder.run_seeder` permission.
|
|
42
|
+
Superusers also need a direct or group grant; their automatic Django permission
|
|
43
|
+
bypass does not apply. Existing users are not granted access on upgrade.
|
|
44
|
+
CLI commands require the explicit allowed environment and do not impersonate a user.
|
|
45
|
+
|
|
46
|
+
After `migrate`, assign **Can run Wagtail Seeder** through Django's user/group
|
|
47
|
+
administration or Wagtail's group permissions. Users also need normal Wagtail
|
|
48
|
+
admin access. Removing the grant immediately hides the menu and blocks seed requests.
|
|
49
|
+
|
|
50
|
+
The dashboard includes a moving loading bar and elapsed time during a seed,
|
|
51
|
+
searchable page selection, and a searchable, sortable result table with status
|
|
52
|
+
filters. The loading bar indicates activity rather than a completion percentage.
|
|
53
|
+
The last report remains available after refreshing until the next seed. Report
|
|
54
|
+
items include `is_summary` (default `False`); summary rows are excluded from
|
|
55
|
+
outcome counts so a failure is counted once.
|
|
56
|
+
|
|
57
|
+
## Sites and languages
|
|
58
|
+
|
|
59
|
+
A single-site project needs only the configuration above. Use `SITE_ROOTS` for
|
|
60
|
+
multiple public sites or languages. Every root creates its own `Site`; descendants,
|
|
61
|
+
page links, settings and translatable snippets stay within their applicable scope.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
WAGTAIL_SEEDER = {
|
|
65
|
+
"ENVIRONMENT": "dev",
|
|
66
|
+
"DEFAULT_SITE_PORT": 80,
|
|
67
|
+
"SITE_ROOTS": [
|
|
68
|
+
{
|
|
69
|
+
"page_model": "pages.HomePage",
|
|
70
|
+
"hostname": "localhost",
|
|
71
|
+
"language_code": "nl",
|
|
72
|
+
"is_default_site": True,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"page_model": "pages.HomePage",
|
|
76
|
+
"hostname": "en.localhost",
|
|
77
|
+
"language_code": "en",
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`language_code` must be in Wagtail's content languages. It resolves or creates a
|
|
84
|
+
`Locale`; it does not enable translation settings or translate text. Omit it to use
|
|
85
|
+
the project's default content language. Do not put a language string in
|
|
86
|
+
`field_values["locale"]`: that field expects a `Locale` instance.
|
|
87
|
+
|
|
88
|
+
Each root requires `page_model`; optional values include `hostname`, `port`,
|
|
89
|
+
`site_name`, `is_default_site`, `slug` and protected `field_values`. Give it a unique
|
|
90
|
+
`key` when another configured root uses `parent_page_key`.
|
|
91
|
+
|
|
92
|
+
`SEED_ROOT_PAGES` uses the same page configuration without creating another Site.
|
|
93
|
+
Use it only for technical branches that otherwise cannot be reached.
|
|
94
|
+
`parent_page_model` or `parent_page_key` deliberately selects an explicit parent;
|
|
95
|
+
`force_root: True` deliberately overrides creation restrictions at the technical
|
|
96
|
+
root. An explicit parent and `force_root` cannot be combined.
|
|
97
|
+
|
|
98
|
+
## Generation and validation
|
|
99
|
+
|
|
100
|
+
The seeder creates draft page structures before filling dependencies and publishing.
|
|
101
|
+
Editor presets keep their existing text and structure while empty required fields
|
|
102
|
+
and nested blocks are filled. Existing valid relationships are preserved, including
|
|
103
|
+
relationships in explicitly declared many-to-many intermediate models.
|
|
104
|
+
Scalar fields required by the page editor are filled even when the model permits
|
|
105
|
+
blank values, without changing the model's field definitions.
|
|
106
|
+
It uses Wagtail's allowed parent/child models, `can_create_at()`, global `max_count`
|
|
107
|
+
and per-parent limits. Titles come from model names: `NewsOverviewPage` becomes
|
|
108
|
+
**News Overview**. Valid defaults and populated content are retained, including
|
|
109
|
+
visibility flags, locales, translation keys and configured values.
|
|
110
|
+
|
|
111
|
+
`PAGE_COUNTS` maps model labels to positive counts **per public tree and locale**;
|
|
112
|
+
the default is one of each applicable concrete type. A subclass does not count as
|
|
113
|
+
its parent type. Wagtail's global singleton limits still take precedence.
|
|
114
|
+
`UNIQUE_PAGE_SCOPES` adds post-seed checks for combinations of `site`, `region`
|
|
115
|
+
and/or `language`.
|
|
116
|
+
|
|
117
|
+
Stream/List blocks and InlinePanels follow their minimum/maximum counts, including
|
|
118
|
+
nested `block_counts`. `REQUIRED_STREAMFIELD_ITEM_COUNTS` supplies additional
|
|
119
|
+
business constraints not declared on the block:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
"REQUIRED_STREAMFIELD_ITEM_COUNTS": {"pages.ArticlePage.highlights": 2}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`SNIPPET_COUNTS` accepts model labels or legacy class names (labels take precedence).
|
|
126
|
+
The default is four; use zero to request no new instances. Translatable snippets
|
|
127
|
+
are generated for every configured root locale. Existing instances are reused and
|
|
128
|
+
validated, and stale chooser links are repaired without replacing their text.
|
|
129
|
+
Site settings are populated before saving, then revisited after their hooks have
|
|
130
|
+
created any additional pages. Page-owned inlines keep their original parent.
|
|
131
|
+
|
|
132
|
+
When `wagtailstreamforms` is installed, the seeder builds local contact forms from
|
|
133
|
+
registered single-line, email and multiline field blocks and configured templates.
|
|
134
|
+
It also supports older installations with separate FormField models. Submission
|
|
135
|
+
hooks stay empty; the seeder does not submit forms or send mail.
|
|
136
|
+
|
|
137
|
+
The inspector only reads configuration and model structure; it does not execute
|
|
138
|
+
model validation or prove rendering works. `seed_wagtail --require-complete` checks
|
|
139
|
+
both that graph and the results of actual model/block validation. Missing mandatory
|
|
140
|
+
content fails the command. Optional unsupported blocks/inlines are reported as
|
|
141
|
+
skipped; declared exclusions are intentional. Failed objects are rolled back,
|
|
142
|
+
while successful objects remain available for diagnosis.
|
|
143
|
+
|
|
144
|
+
## Small project providers
|
|
145
|
+
|
|
146
|
+
Use providers for business rules that cannot be inferred from field declarations.
|
|
147
|
+
Keep them in the consuming project and configure dotted paths:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
"MODEL_VALUE_PROVIDERS": ["myproject.seeding.model_values"],
|
|
151
|
+
"FIELD_VALUE_PROVIDERS": ["myproject.seeding.field_values"],
|
|
152
|
+
"BLOCK_VALUE_PROVIDERS": ["myproject.seeding.block_values"],
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Model providers receive `(instance, context)` during `prepare` (before generic
|
|
156
|
+
population) and `validate` (before validation). Make them idempotent. Field providers
|
|
157
|
+
receive `(instance, field, context)` during `pre`/`post`, including relation and
|
|
158
|
+
StreamField fields. Block providers receive `(block, context)`. Field and block
|
|
159
|
+
providers return `wagtail_seeder.providers.UNHANDLED` to delegate generation;
|
|
160
|
+
returning `None` intentionally supplies an empty value.
|
|
161
|
+
|
|
162
|
+
Context includes `seeder`, `config`, scoped `pages`, `phase`, `instance`/`owner`,
|
|
163
|
+
`site`, `locale`, a zero-based `index`, `field_name` and a tuple `block_path`.
|
|
164
|
+
For model providers, `index` is stable within the model's ordered instances and
|
|
165
|
+
locale scope. For nested block providers it identifies the current stream/list item.
|
|
166
|
+
Protected `field_values` bypass generic field generation and link repair; model
|
|
167
|
+
providers may deliberately change them.
|
|
168
|
+
|
|
169
|
+
For example, a project that retrieves exactly one navigation object per language
|
|
170
|
+
can assign the configured languages using the model provider's index. Match
|
|
171
|
+
`SNIPPET_COUNTS` to the number of languages. See the
|
|
172
|
+
[configuration guide](docs/compatibility.md) for generic examples covering multiple
|
|
173
|
+
sites, custom validation, technical page types and external dependencies.
|
|
174
|
+
|
|
175
|
+
## External integrations
|
|
176
|
+
|
|
177
|
+
Exclude models and blocks whose validation or rendering needs an external service:
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
"EXCLUDED_SNIPPET_MODELS": ["newsletter.MailingList"],
|
|
181
|
+
"SKIP_BLOCK_CLASS_NAMES": {"NewsletterBlock": "requires an external service"},
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Exclusions also apply to indirect chooser/required-relation creation. Block exclusions
|
|
185
|
+
match class names, qualified names and subclasses. A required excluded dependency
|
|
186
|
+
is an error; a provider or a different valid local block is needed. External optional
|
|
187
|
+
video URL fields with empty valid defaults stay empty. Seeder settings cannot turn
|
|
188
|
+
off network calls hardcoded in a project's page rendering or save hooks.
|
|
189
|
+
|
|
190
|
+
Other optional keys are `ALLOWED_ENVIRONMENTS` (default `{"dev"}`),
|
|
191
|
+
`ALLOWED_HOSTNAMES` (dashboard request guard), `FAKER_LOCALE` (default `"nl_NL"`),
|
|
192
|
+
`DEFAULT_SITE_HOSTNAME` (default `"localhost"`) and `DEFAULT_SITE_PORT`
|
|
193
|
+
(default `8000`). Complete local compatibility requires testing the generated
|
|
194
|
+
frontend and editor in the consuming project; static inspection cannot guarantee
|
|
195
|
+
arbitrary custom validation or JavaScript behavior.
|
|
196
|
+
|
|
197
|
+
## Development and testing
|
|
198
|
+
|
|
199
|
+
The repository separates the distributable package from its local test project:
|
|
200
|
+
|
|
201
|
+
```text
|
|
202
|
+
src/wagtail_seeder/ Python package, management commands and admin templates
|
|
203
|
+
tests/test_project/ Runnable Django/Wagtail project, local database and media
|
|
204
|
+
tests/models.py Example page and relation models
|
|
205
|
+
tests/migrations/ One initial migration for the example models
|
|
206
|
+
tests/test_*.py Automated tests
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
From the repository root, create an environment and install the package in editable
|
|
210
|
+
mode. Changes in `src` then take effect immediately:
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
python -m venv .venv
|
|
214
|
+
source .venv/bin/activate
|
|
215
|
+
python -m pip install -e ".[dev]"
|
|
216
|
+
pytest
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
The default installation uses the latest available Wagtail 7.x. To test the lowest
|
|
220
|
+
supported release series, use a separate environment and install with
|
|
221
|
+
`python -m pip install -e ".[dev]" "wagtail>=7,<7.1"`.
|
|
222
|
+
|
|
223
|
+
Run the same checks as CI:
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
python tests/test_project/manage.py check
|
|
227
|
+
python tests/test_project/manage.py makemigrations --check --dry-run
|
|
228
|
+
ruff check .
|
|
229
|
+
ruff format --check .
|
|
230
|
+
pytest
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Automated tests use an isolated database and temporary media directories. They do
|
|
234
|
+
not change the local demo database or leave generated media in the repository root.
|
|
235
|
+
|
|
236
|
+
## Local Wagtail project
|
|
237
|
+
|
|
238
|
+
No external project or Bakery Demo checkout is required. From the repository root:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
python tests/test_project/manage.py migrate
|
|
242
|
+
python tests/test_project/manage.py createsuperuser
|
|
243
|
+
python tests/test_project/manage.py inspect_wagtail_seed --require-complete
|
|
244
|
+
python tests/test_project/manage.py seed_wagtail --confirm --require-complete
|
|
245
|
+
python tests/test_project/manage.py runserver
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Open `http://127.0.0.1:8000/admin/` and log in. Explicitly assign **Can run Wagtail
|
|
249
|
+
Seeder** to the account or its group before opening **Seeder** in the menu.
|
|
250
|
+
Visit `http://127.0.0.1:8000/admin/seeder/` to test a full seed or
|
|
251
|
+
selected page types. The confirmation checkbox is required for a destructive full
|
|
252
|
+
seed. Open `http://127.0.0.1:8000/` to browse the generated pages.
|
|
253
|
+
|
|
254
|
+
For example, test the selective command with:
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
python tests/test_project/manage.py seed_wagtail_pages --page-type tests.ArticlePage
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
The local database, uploaded/generated media and collected static files live under
|
|
261
|
+
`tests/test_project/` and are ignored by Git. The project is configured for local
|
|
262
|
+
development only.
|
|
263
|
+
The example models use one initial migration. If a local demo database was created
|
|
264
|
+
with the previous migration layout, recreate that demo database before migrating.
|
|
265
|
+
Installed packages continue to use the separate `wagtail_seeder` permission migration.
|
|
266
|
+
|
|
267
|
+
## Building and releasing
|
|
268
|
+
|
|
269
|
+
Build the wheel and source distribution and check their metadata:
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
python -m build
|
|
273
|
+
python -m twine check dist/*
|
|
274
|
+
python scripts/check_distribution.py
|
|
275
|
+
tar -tf dist/wagtail_seeder-*.tar.gz
|
|
276
|
+
unzip -l dist/wagtail_seeder-*.whl
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Before publishing, install the built wheel in the target project's environment
|
|
280
|
+
with `python -m pip install /path/to/wagtail_seeder-1.0.0-py3-none-any.whl`, then
|
|
281
|
+
apply migrations and the local Seeder configuration. Check a full seed, selective
|
|
282
|
+
seeding, frontend rendering and saving generated pages in the editor. This tests
|
|
283
|
+
the packaged assets and any custom validation in the application.
|
|
284
|
+
|
|
285
|
+
Runtime code and templates come from `src/wagtail_seeder/`. Tests, demo databases
|
|
286
|
+
and media are excluded from both distributions. The source archive also contains
|
|
287
|
+
the README, license and build metadata.
|
|
288
|
+
|
|
289
|
+
Public releases are published from GitLab CI to PyPI using
|
|
290
|
+
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/using-a-publisher/).
|
|
291
|
+
Before the first release, configure a pending publisher on PyPI (or a publisher
|
|
292
|
+
on the existing project) with:
|
|
293
|
+
|
|
294
|
+
- PyPI project: `wagtail-seeder`
|
|
295
|
+
- GitLab namespace: `fourdigits/private-packages`
|
|
296
|
+
- GitLab project: `wagtail-seeder`
|
|
297
|
+
- Pipeline file: `.gitlab-ci.yml`
|
|
298
|
+
- Environment: `pypi`
|
|
299
|
+
|
|
300
|
+
Protect the `v*` tag pattern in GitLab. No stored PyPI API token is needed.
|
|
301
|
+
Update the version in the project metadata and
|
|
302
|
+
`src/wagtail_seeder/__init__.py`, then push a protected tag matching that version,
|
|
303
|
+
for example `v1.0.0`. CI requires the tag and both version declarations to agree,
|
|
304
|
+
and publishes only after tests and package checks pass. Published versions cannot
|
|
305
|
+
be overwritten; bump the version for each new release.
|
|
306
|
+
|
|
307
|
+
## License
|
|
308
|
+
|
|
309
|
+
BSD-3-Clause. See [LICENSE](LICENSE).
|