Skip to content

Commit 40bc235

Browse files
committed
Review feedback and various fixes
1 parent b2a03e1 commit 40bc235

File tree

4 files changed

+72
-42
lines changed

4 files changed

+72
-42
lines changed

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ for more examples.
2222

2323
``` python
2424
import wagtail_factories
25+
from wagtail import models as wt_models
26+
2527
from . import models
2628

2729

@@ -63,9 +65,12 @@ class MyTestPageFactory(wagtail_factories.PageFactory):
6365

6466

6567
def test_my_page():
66-
root_page = wagtail_factories.PageFactory(parent=None)
68+
root_page = wt_models.Page.get_first_root_node()
69+
assert root_page is not None
70+
6771
my_page = MyTestPageFactory(
6872
parent=root_page,
73+
title="My great page",
6974
body__0__carousel__items__0__label='Slide 1',
7075
body__0__carousel__items__0__image__image__title='Image Slide 1',
7176
body__0__carousel__items__1__label='Slide 2',
@@ -74,6 +79,13 @@ def test_my_page():
7479
body__0__carousel__items__2__image__image__title='Image Slide 3',
7580
body__1__news_page__page__title="News",
7681
)
82+
83+
# Defaults defined on factory classes are propagated.
84+
assert my_page.body[0].value["title"] == "Carousel title"
85+
86+
# Parameters are propagated.
87+
assert my_page.title == "My great page"
88+
assert my_page.body[0].value["items"][0].value["label"] == "Slide 1"
7789
```
7890

7991
### Using StreamBlockFactory

docs/architecture.rst

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ System components
99

1010
wagtail-factories extends Factory Boy to create test data for Wagtail CMS models. The system consists of three main layers:
1111

12-
**Factory layer**
12+
**Model factory layer**
1313
Core factory classes that extend Factory Boy's ``DjangoModelFactory`` for Wagtail-specific models (``PageFactory``, ``ImageFactory``, etc.)
1414

1515
**Block factory layer**
@@ -85,7 +85,7 @@ StreamField factories are defined using class-based syntax that mirrors Wagtail'
8585
class Meta:
8686
model = MyStreamBlock
8787

88-
This approach enables nested StreamBlocks, better IDE support, and cleaner factory composition.
88+
This approach enables nested StreamBlocks and clean, introspectable factory composition.
8989

9090
.. note::
9191

@@ -156,4 +156,8 @@ This allows adaptation to domain-specific Wagtail block types while maintaining
156156
Next steps
157157
==========
158158

159-
**For contributors**: If you need to modify or extend the StreamField factory system, see :doc:`streamfield-internals` for detailed technical implementation details including Factory Boy integration mechanisms, parameter parsing, and builder system architecture.
159+
**For contributors**: If you need to modify or extend the StreamField factory system, see :doc:`streamfield-internals` for detailed technical implementation details, including:
160+
161+
- Factory Boy integration mechanisms;
162+
- parameter parsing; and
163+
- builder system architecture.

docs/getting-started.rst

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ Usage
1616
.. code-block:: python
1717
1818
import wagtail_factories
19+
from wagtail import models as wt_models
20+
1921
from . import models
2022
2123
@@ -46,13 +48,23 @@ Usage
4648
4749
4850
def test_my_page():
49-
root_page = wagtail_factories.PageFactory(parent=None)
51+
root_page = wt_models.Page.get_first_root_node()
52+
assert root_page is not None
53+
5054
my_page = MyTestPageFactory(
5155
parent=root_page,
56+
title="My great page",
5257
body__0__carousel__items__0__label='Slide 1',
5358
body__0__carousel__items__0__image__image__title='Image Slide 1',
5459
body__0__carousel__items__1__label='Slide 2',
5560
body__0__carousel__items__1__image__image__title='Image Slide 2',
5661
body__0__carousel__items__2__label='Slide 3',
5762
body__0__carousel__items__2__image__image__title='Image Slide 3',
58-
)
63+
)
64+
65+
# Defaults defined on factory classes are propagated.
66+
assert my_page.body[0].value["title"] == "Carousel title"
67+
68+
# Parameters are propagated.
69+
assert my_page.title == "My great page"
70+
assert my_page.body[0].value["items"][0].value["label"] == "Slide 1"

docs/streamfield-internals.rst

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,26 @@ Factory definitions
5151
.. code-block:: python
5252
5353
# Define the block structure
54-
class MyStreamBlockFactory(StreamBlockFactory):
55-
text_block = factory.SubFactory(CharBlockFactory)
56-
struct_block = factory.SubFactory(MyStructBlockFactory)
57-
58-
class Meta:
59-
model = MyStreamBlock
60-
6154
class MyStructBlockFactory(StructBlockFactory):
6255
title = "Default Title"
6356
content = "Default Content"
64-
57+
6558
class Meta:
6659
model = MyStructBlock
67-
60+
61+
62+
class MyStreamBlockFactory(StreamBlockFactory):
63+
text_block = factory.SubFactory(CharBlockFactory)
64+
struct_block = factory.SubFactory(MyStructBlockFactory)
65+
66+
class Meta:
67+
model = MyStreamBlock
68+
69+
6870
# Use in page factory
6971
class MyPageFactory(factory.Factory):
7072
body = StreamFieldFactory(MyStreamBlockFactory)
71-
73+
7274
class Meta:
7375
model = MyPage
7476
@@ -194,7 +196,7 @@ When a StreamBlock contains multiple instances of the same block type (e.g., ``0
194196
Factory Boy DeclarationSet compatibility
195197
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
196198

197-
Factory Boy uses a DeclarationSet to store factory attributes like ``title = "Hello"``. Each key must be unique and hashable. Using ``0.struct_block__title`` as the key allows the system to associate parameters with the correct generated factory field during dynamic factory creation.
199+
Factory Boy uses a DeclarationSet to store factory attributes like ``title = "Hello"``. Each key must be unique and hashable. Using ``0.struct_block__title`` as the key allows the system to associate parameters with the correct generated factory field during dynamic factory creation (see `Phase 2: Generate a Factory class dynamically`_).
198200

199201
Prevents Factory Boy field interpretation errors
200202
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -293,7 +295,7 @@ Factory Boy handles the initial parameter filtering automatically. The ``Paramet
293295

294296
1. User calls ``MyPageFactory(body__0__struct_block__title="foo")``
295297
2. Factory Boy processes: Identifies ``body__`` prefix matches ``StreamFieldFactory``
296-
3. Factory Boy strips: Removes ``body__`` prefix from matching parameters
298+
3. Factory Boy strips: Removes ``body__`` prefix from matching parameters
297299
4. Our code receives: ``StreamFieldFactory.evaluate()`` gets ``{'0__struct_block__title': 'foo'}``
298300

299301
This timing is crucial - our parameter parsing code never sees the original full parameter names.
@@ -315,13 +317,13 @@ Example: ``body__0__struct_block__title="Hello"``
315317
├── Receives: body__0__struct_block__title="Hello"
316318
├── StreamFieldFactory "body" matches prefix
317319
├── Strips "body__" → delegates: 0__struct_block__title="Hello"
318-
319-
Level 2: StreamBlockFactory
320+
321+
Level 2: StreamBlockFactory
320322
├── Receives: 0__struct_block__title="Hello"
321323
├── Builder parses: index=0, block="struct_block", params=["title"]
322-
├── Creates SubFactory for StructBlockFactory
324+
├── Delegates to SubFactory for StructBlockFactory
323325
├── Delegates: title="Hello"
324-
326+
325327
Level 3: StructBlockFactory
326328
├── Receives: title="Hello"
327329
├── Sets struct field directly
@@ -336,7 +338,7 @@ Critical flow points
336338
---------------------
337339

338340
1. Entry point filtering: Factory Boy automatically filters parameters by prefix for each ``StreamFieldFactory``
339-
2. Prefix stripping: Essential for clean delegation to child factories
341+
2. Prefix stripping: Essential for clean delegation to child factories
340342
3. Recursive parsing: Each builder level handles one level of nesting
341343
4. Parameter transformation: Keys get transformed for Factory Boy compatibility (``0.struct_block__title``)
342344
5. Factory Boy delegation: Standard ``SubFactory`` mechanisms handle the final construction
@@ -358,10 +360,10 @@ Simple nesting
358360
│ └─ filters for "body__" prefix
359361
│ └─ delegates: {"0__struct_block__title": "Hello"}
360362
361-
├─ StreamBlockFactory via StreamBlockStepBuilder
363+
├─ StreamBlockFactory via StreamBlockStepBuilder
362364
│ └─ receives: {"0__struct_block__title": "Hello"}
363365
│ └─ parses: index=0, block="struct_block", field="title"
364-
│ └─ creates: SubFactory(StructBlockFactory, title="Hello")
366+
│ └─ reuses: existing SubFactory(StructBlockFactory) with title="Hello"
365367
366368
└─ StructBlockFactory
367369
└─ receives: {"title": "Hello"}
@@ -381,16 +383,16 @@ Deep nesting
381383
382384
├─ StreamBlockStepBuilder (level 1)
383385
│ └─ parses: index=0, block="struct", remaining="inner_stream__1__char_block"
384-
│ └─ creates: SubFactory(StructBlockFactory, inner_stream__1__char_block="text")
386+
│ └─ reuses: existing SubFactory(StructBlockFactory) with inner_stream__1__char_block="text"
385387
386-
├─ StructBlockFactory
388+
├─ StructBlockFactory
387389
│ └─ receives: {"inner_stream__1__char_block": "text"}
388390
│ └─ has inner_stream = StreamFieldFactory(...)
389391
│ └─ delegates: {"1__char_block": "text"}
390392
391393
├─ StreamBlockStepBuilder (level 2)
392394
│ └─ parses: index=1, block="char_block", remaining=""
393-
│ └─ creates: SubFactory(CharBlockFactory, value="text")
395+
│ └─ reuses: existing SubFactory(CharBlockFactory) with value="text"
394396
395397
└─ CharBlockFactory
396398
└─ receives: {"value": "text"} (or direct assignment)
@@ -407,19 +409,19 @@ Based on verified execution tracing, this complex flow involves multiple builder
407409
408410
Level 1: StreamFieldFactory.evaluate()
409411
└─ receives: {'0__list_block__0__0__struct_block__title': 'foo'}
410-
412+
411413
Level 2: StreamBlockStepBuilder (outer)
412414
└─ parses: index=0, block='list_block'
413415
└─ delegates: {'0__0__struct_block__title': 'foo'} to ListBlockFactory
414-
415-
Level 3: ListBlockFactory.evaluate()
416+
417+
Level 3: ListBlockFactory.evaluate()
416418
└─ groups by list index: result[0] = {'0__struct_block__title': 'foo'}
417-
└─ calls step.recurse() → creates child StreamBlockFactory
418-
419+
└─ calls step.recurse() → creates child StreamBlockFactory dynamically
420+
419421
Level 4: StreamBlockStepBuilder (inner)
420422
└─ receives: {'0__struct_block__title': 'foo'}
421423
└─ parses: index=0, block='struct_block'
422-
└─ creates: StructBlockFactory with title='foo'
424+
└─ reuses: existing StructBlockFactory with title='foo'
423425
424426
425427
Block factory behavior
@@ -497,7 +499,7 @@ Declaration syntax
497499
~~~~~~~~~~~~~~~~~~
498500

499501
- ``items__0__label="foo"`` - Set field in first StructBlock item
500-
- ``char_array__0="hello"`` - Set first item in CharBlock list
502+
- ``char_array__0="hello"`` - Set first item in CharBlock list
501503
- ``list_block__0__0__struct_block__title="foo"`` - ListBlock containing StreamBlocks
502504

503505
Implementation
@@ -550,7 +552,7 @@ Initialization patterns
550552
"block_name": BlockFactory,
551553
})
552554
553-
# Class-based (recommended)
555+
# Class-based (recommended)
554556
body = StreamFieldFactory(MyStreamBlockFactory)
555557
556558
Block definition instantiation
@@ -623,7 +625,7 @@ Specialized options for StreamBlock factories with advanced parameter filtering:
623625
block_name = get_block_name(k)
624626
if (
625627
block_name not in self.exclude
626-
and block_name not in self.parameters
628+
and block_name not in self.parameters
627629
and v is not declarations.SKIP
628630
):
629631
filtered_kwargs[k] = v
@@ -663,21 +665,21 @@ Explicit block definition
663665
block_def = MyStreamBlock() # Explicit
664666
665667
Auto-instantiation (more common)
666-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
668+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
667669

668670
.. code-block:: python
669671
670-
class MyStreamBlockFactory(StreamBlockFactory):
672+
class MyStreamBlockFactory(StreamBlockFactory):
671673
class Meta:
672674
model = MyStreamBlock # Auto-instantiated when needed
673675
674676
.. important::
675677
**Why block definitions matter**
676-
678+
677679
Wagtail block definitions are required to construct proper ``StreamValue`` objects. Without them, the system falls back to returning raw data structures. The options system ensures block definitions are available throughout the factory hierarchy by:
678-
680+
679681
- Auto-instantiating models when needed
680-
- Propagating definitions through ``SubFactory`` chains
682+
- Propagating definitions through ``SubFactory`` chains
681683
- Providing consistent access via ``get_block_definition()``
682684

683685
Error handling and validation

0 commit comments

Comments
 (0)