Skip to content

Commit 33805b1

Browse files
committed
docs: Add documentation for post_build and post_generate hooks
Added comprehensive documentation for the new lifecycle hooks: - Created two example files demonstrating post_build and post_generate usage - Added "Lifecycle Hooks" section to configuration.rst documentation - Examples show practical use cases: - post_build: Modifying created instances (e.g., auto-verifying test accounts) - post_generate: Computing derived fields before model creation (e.g., calculating total price with tax) Addresses reviewer feedback on PR litestar-org#667 requesting documentation.
1 parent c68e79f commit 33805b1

File tree

3 files changed

+123
-0
lines changed

3 files changed

+123
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
from dataclasses import dataclass
2+
3+
from polyfactory.factories import DataclassFactory
4+
5+
6+
@dataclass
7+
class Account:
8+
"""User account model."""
9+
10+
username: str
11+
email: str
12+
is_verified: bool
13+
verification_token: str
14+
15+
16+
class AccountFactory(DataclassFactory[Account]):
17+
"""Factory for Account with post_build hook."""
18+
19+
@classmethod
20+
def post_build(cls, account: Account) -> Account:
21+
"""Post-build hook to modify the created instance.
22+
23+
This hook is called after the model instance is created,
24+
allowing you to run custom logic on the fully-generated object.
25+
"""
26+
# Auto-verify accounts in test environment
27+
if account.username.startswith("test_"):
28+
account.is_verified = True
29+
account.verification_token = ""
30+
31+
return account
32+
33+
34+
def test_post_build_hook() -> None:
35+
# Regular account will have random is_verified value
36+
regular_account = AccountFactory.build(username="john_doe")
37+
assert regular_account.username == "john_doe"
38+
39+
# Test account will be auto-verified by the post_build hook
40+
test_account = AccountFactory.build(username="test_user")
41+
assert test_account.is_verified is True
42+
assert test_account.verification_token == ""
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
from dataclasses import dataclass
2+
from typing import Any, Dict
3+
4+
from polyfactory.factories import DataclassFactory
5+
6+
7+
@dataclass
8+
class Product:
9+
"""Product model."""
10+
11+
name: str
12+
price: float
13+
tax_rate: float
14+
total_price: float
15+
16+
17+
class ProductFactory(DataclassFactory[Product]):
18+
"""Factory for Product with post_generate hook."""
19+
20+
@classmethod
21+
def post_generate(cls, result: Dict[str, Any]) -> Dict[str, Any]:
22+
"""Post-generate hook to modify kwargs before model creation.
23+
24+
This hook is called after field values are generated but before
25+
the model instance is created, allowing you to compute derived
26+
fields based on other generated values.
27+
"""
28+
# Calculate total_price based on price and tax_rate
29+
price = result["price"]
30+
tax_rate = result["tax_rate"]
31+
result["total_price"] = price * (1 + tax_rate)
32+
33+
return result
34+
35+
36+
def test_post_generate_hook() -> None:
37+
product = ProductFactory.build()
38+
39+
# Verify total_price was calculated correctly
40+
expected_total = product.price * (1 + product.tax_rate)
41+
assert abs(product.total_price - expected_total) < 0.01
42+
43+
# Verify with specific values
44+
product = ProductFactory.build(price=100.0, tax_rate=0.2)
45+
assert product.total_price == 120.0

docs/usage/configuration.rst

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,39 @@ forward reference names to their resolved types.
164164
.. note::
165165
The Pydantic ModelFactory has a default forward reference mapping for ``JsonValue`` to resolve to ``str``
166166
to avoid recursive issues with Pydantic's JsonValue type.
167+
168+
Lifecycle Hooks
169+
---------------
170+
171+
Factories provide two lifecycle hooks that allow you to customize the model creation process:
172+
173+
Post-Build Hook
174+
^^^^^^^^^^^^^^^^
175+
176+
The :meth:`~polyfactory.factories.base.BaseFactory.post_build` hook is called after a model instance
177+
is created. This is useful for running custom logic on the fully-generated object, such as setting up
178+
relationships, calculating derived fields, or modifying the instance based on its generated values.
179+
180+
.. literalinclude:: /examples/configuration/test_example_12.py
181+
:caption: Using the post_build hook
182+
:language: python
183+
184+
The ``post_build`` method receives the created model instance and must return it (potentially modified).
185+
186+
Post-Generate Hook
187+
^^^^^^^^^^^^^^^^^^^
188+
189+
The :meth:`~polyfactory.factories.base.BaseFactory.post_generate` hook is called after field values
190+
are generated but before the model instance is created. This allows you to modify the kwargs dictionary
191+
that will be passed to the model constructor, making it ideal for computing derived fields or ensuring
192+
consistency between related fields.
193+
194+
.. literalinclude:: /examples/configuration/test_example_13.py
195+
:caption: Using the post_generate hook
196+
:language: python
197+
198+
The ``post_generate`` method receives the generated kwargs dictionary and must return it (potentially modified).
199+
200+
.. note::
201+
Both hooks are classmethods that can be overridden in your factory class. They are called automatically
202+
by ``build()``, ``batch()``, and all persistence methods.

0 commit comments

Comments
 (0)