Metadata-Version: 2.4
Name: wagtail_factories
Version: 4.5.0
Summary: Factory boy classes for wagtail
Home-page: https://github.com/wagtail/wagtail-factories/
Author: Michael van Tellingen
Author-email: michaelvantellingen@gmail.com
License: MIT
Classifier: Development Status :: 5 - Production/Stable
Classifier: License :: OSI Approved :: MIT License
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Framework :: Django
Classifier: Framework :: Django :: 4.2
Classifier: Framework :: Django :: 5.2
Classifier: Framework :: Django :: 6.0
Classifier: Framework :: Wagtail
Classifier: Framework :: Wagtail :: 7
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: factory-boy<4,>=3.2
Requires-Dist: wagtail>=7.0
Provides-Extra: docs
Requires-Dist: sphinx>=8.2.3; extra == "docs"
Provides-Extra: test
Requires-Dist: pytest==9.0.3; extra == "test"
Requires-Dist: pytest-django==4.12.0; extra == "test"
Requires-Dist: pytest-cov==7.1.0; extra == "test"
Requires-Dist: coverage==7.13.5; extra == "test"
Requires-Dist: ruff==0.15.12; extra == "test"
Requires-Dist: tox==4.32.0; extra == "test"
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: license-file
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# wagtail-factories

Factory boy classes for Wagtail CMS

## Installation

``` shell
pip install wagtail-factories
```

## Usage

Documentation is still in progress, but see the
[tests](https://github.com/wagtail/wagtail-factories/tree/main/tests)
for more examples.

``` python
import wagtail_factories
from wagtail import models as wt_models

from . import models


class MyCarouselItemFactory(wagtail_factories.StructBlockFactory):
    label = 'my-label'
    image = factory.SubFactory(
        wagtail_factories.ImageChooserBlockFactory)

    class Meta:
        model = models.MyBlockItem


class MyCarouselFactory(wagtail_factories.StructBlockFactory):
    title = "Carousel title"
    items = wagtail_factories.ListBlockFactory(
        MyCarouselItemFactory)

    class Meta:
        model = models.MyCarousel


class MyNewsPageFactory(wagtail_factories.PageFactory):
    class Meta:
        model = models.MyNewsPage


class MyNewsPageChooserBlockFactory(wagtail_factories.PageChooserBlockFactory):
    page = factory.SubFactory(MyNewsPageFactory)


class MyTestPageFactory(wagtail_factories.PageFactory):
    body = wagtail_factories.StreamFieldFactory({
        'carousel': factory.SubFactory(MyCarouselFactory),
        'news_page': factory.SubFactory(MyNewsPageChooserBlockFactory),
    })

    class Meta:
        model = models.MyTestPage


def test_my_page():
    root_page = wt_models.Page.get_first_root_node()
    assert root_page is not None

    my_page = MyTestPageFactory(
        parent=root_page,
        title="My great page",
        body__0__carousel__items__0__label='Slide 1',
        body__0__carousel__items__0__image__image__title='Image Slide 1',
        body__0__carousel__items__1__label='Slide 2',
        body__0__carousel__items__1__image__image__title='Image Slide 2',
        body__0__carousel__items__2__label='Slide 3',
        body__0__carousel__items__2__image__image__title='Image Slide 3',
        body__1__news_page__page__title="News",
    )

    # Defaults defined on factory classes are propagated.
    assert my_page.body[0].value["title"] == "Carousel title"

    # Parameters are propagated.
    assert my_page.title == "My great page"
    assert my_page.body[0].value["items"][0].value["label"] == "Slide 1"
```

### Using StreamBlockFactory

`StreamBlockFactory` can be used in conjunction with the other block
factory types to create complex, nested `StreamValues`, much like how
`StreamBlock` can be used to declare the blocks for a complex
`StreamField`.

First, define your `StreamBlockFactory` subclass, using
`factory.SubFactory` to wrap child block declarations. Be sure to
include your `StreamBlock` subclass as the model attribute on the inner
`Meta` class.

``` python
class MyStreamBlockFactory(wagtail_factories.StreamBlockFactory):
    my_struct_block = factory.SubFactory(MyStructBlockFactory)

    class Meta:
        model = MyStreamBlock
```

Then include your `StreamBlockFactory` subclass on a model factory as
the argument to a `StreamFieldFactory`.

``` python
class MyPageFactory(wagtail_factories.PageFactory):
    body = wagtail_factories.StreamFieldFactory(MyStreamBlockFactory)

    class Meta:
        model = MyPage
```

You can then use a modified version of factory\_boy\'s deep object
declaration syntax to build up `StreamValues` on the fly.

``` python
MyPageFactory(
    body__0__my_struct_block__some_field="some value",
    body__0__my_struct_block__some_other_field="some other value",
)
```

To generate the default value for a block factory, terminate your
declaration at the index and provide the block name as the value.

``` python
MyPageFactory(body__0="my_struct_block")
```

### Alternative StreamFieldFactory declaration syntax

Prior to version 3.0, `StreamFieldFactory` could only be used by
providing a dict mapping block names to block factory classes as the
single argument, for example:

``` python
class MyTestPageWithStreamFieldFactory(wagtail_factories.PageFactory):
    body = wagtail_factories.StreamFieldFactory(
        {
            "char_array": wagtail_factories.ListBlockFactory(
                wagtail_factories.CharBlockFactory
            ),
            "int_array": wagtail_factories.ListBlockFactory(
                wagtail_factories.IntegerBlockFactory
            ),
            "struct": MyBlockFactory,
            "image": wagtail_factories.ImageChooserBlockFactory,
        }
    )

    class Meta:
        model = models.MyTestPage
```

This style of declaration is still supported, with the caveat that
nested stream blocks are not supported for this approach. From version
3.0, all `BlockFactory` values in a `StreamFieldFactory` definition of
this style *must* be wrapped in factory\_boy `SubFactories`. For
example, the above example must be updated to the following for 3.0
compatibility.

``` python
class MyTestPageWithStreamFieldFactory(wagtail_factories.PageFactory):
    body = wagtail_factories.StreamFieldFactory(
        {
            "char_array": wagtail_factories.ListBlockFactory(
                wagtail_factories.CharBlockFactory
            ),
            "int_array": wagtail_factories.ListBlockFactory(
                wagtail_factories.IntegerBlockFactory
            ),
            "struct": factory.SubFactory(MyBlockFactory),
            "image": factory.SubFactory(wagtail_factories.ImageChooserBlockFactory),
        }
    )

    class Meta:
        model = models.MyTestPage
```

This requirement does *not* apply to `ListBlockFactory`, which is a
subclass of `SubFactory`.
