0
# Attribute Declarations
1
2
Comprehensive declaration types for generating dynamic attribute values in factories. These declarations enable sophisticated test data generation with sequences, lazy evaluation, faker integration, conditional logic, and complex object relationships.
3
4
## Capabilities
5
6
### Basic Attribute Declarations
7
8
Core declarations for computing attribute values using functions and external data.
9
10
```python { .api }
11
class LazyFunction:
12
"""
13
Computed by calling function without arguments each time.
14
15
Args:
16
function: Callable that returns the attribute value
17
"""
18
def __init__(self, function): ...
19
20
class LazyAttribute:
21
"""
22
Computed using function that receives the current instance being built.
23
24
Args:
25
function: Callable that takes the instance and returns attribute value
26
"""
27
def __init__(self, function): ...
28
29
class SelfAttribute:
30
"""
31
Copy values from other fields, supports dot notation and parent access.
32
33
Args:
34
attribute_name (str): Name of attribute to copy (supports 'field', 'sub.field', '..parent.field')
35
default: Default value if attribute not found
36
"""
37
def __init__(self, attribute_name, default=UNSPECIFIED): ...
38
```
39
40
#### Usage Examples
41
42
```python
43
class UserFactory(Factory):
44
class Meta:
45
model = User
46
47
# LazyFunction - called fresh each time
48
created_at = LazyFunction(lambda: timezone.now())
49
uuid = LazyFunction(lambda: str(uuid.uuid4()))
50
51
# LazyAttribute - receives the current instance
52
email = LazyAttribute(lambda obj: f'{obj.username}@example.com')
53
display_name = LazyAttribute(lambda obj: f'{obj.first_name} {obj.last_name}')
54
55
# SelfAttribute - copy from other fields
56
username = 'john_doe'
57
login_name = SelfAttribute('username')
58
backup_email = SelfAttribute('email', default='fallback@example.com')
59
```
60
61
### Sequence and Iterator Declarations
62
63
Declarations for generating unique, sequential, or cyclic values.
64
65
```python { .api }
66
class Sequence:
67
"""
68
Generate increasing unique values using sequence counter.
69
70
Args:
71
function: Callable that takes sequence number (int) and returns value
72
"""
73
def __init__(self, function): ...
74
75
class LazyAttributeSequence:
76
"""
77
Combination of LazyAttribute and Sequence - receives instance and sequence number.
78
79
Args:
80
function: Callable that takes (instance, sequence_number) and returns value
81
"""
82
def __init__(self, function): ...
83
84
class Iterator:
85
"""
86
Fill value using iterator values, with optional cycling and getter function.
87
88
Args:
89
iterator: Iterable to get values from
90
cycle (bool): Whether to cycle through values when exhausted
91
getter: Optional function to extract values from iterator items
92
"""
93
def __init__(self, iterator, cycle=True, getter=None): ...
94
95
def reset(self):
96
"""Reset internal iterator to beginning."""
97
```
98
99
#### Usage Examples
100
101
```python
102
class UserFactory(Factory):
103
class Meta:
104
model = User
105
106
# Simple sequence
107
email = Sequence(lambda n: f'user{n}@example.com')
108
109
# LazyAttributeSequence - instance + sequence
110
username = LazyAttributeSequence(lambda obj, n: f'{obj.first_name.lower()}_{n}')
111
112
# Iterator with cycling
113
department = Iterator(['Engineering', 'Sales', 'Marketing'])
114
115
# Iterator with getter function
116
priority = Iterator([
117
{'name': 'High', 'value': 1},
118
{'name': 'Medium', 'value': 2},
119
{'name': 'Low', 'value': 3}
120
], getter=lambda item: item['value'])
121
122
# Non-cycling iterator
123
one_time_code = Iterator(['ABC123', 'DEF456', 'GHI789'], cycle=False)
124
```
125
126
### Faker Integration
127
128
Integration with the Faker library for generating realistic fake data.
129
130
```python { .api }
131
class Faker:
132
"""
133
Wrapper for faker library values with locale support and custom providers.
134
135
Args:
136
provider (str): Faker provider name (e.g., 'name', 'email', 'address')
137
locale (str, optional): Locale for this faker instance
138
**kwargs: Additional arguments passed to the faker provider
139
"""
140
def __init__(self, provider, locale=None, **kwargs): ...
141
142
def generate(self, extra_kwargs=None):
143
"""Generate fake value with optional extra parameters."""
144
145
@classmethod
146
def override_default_locale(cls, locale):
147
"""Context manager for temporarily overriding default locale."""
148
149
@classmethod
150
def add_provider(cls, provider, locale=None):
151
"""Add custom faker provider."""
152
```
153
154
#### Usage Examples
155
156
```python
157
class UserFactory(Factory):
158
class Meta:
159
model = User
160
161
# Basic faker usage
162
first_name = Faker('first_name')
163
last_name = Faker('last_name')
164
email = Faker('email')
165
166
# Faker with parameters
167
birthdate = Faker('date_of_birth', minimum_age=18, maximum_age=65)
168
bio = Faker('text', max_nb_chars=200)
169
170
# Locale-specific faker
171
phone = Faker('phone_number', locale='en_US')
172
173
# Using context manager for locale
174
with Faker.override_default_locale('fr_FR'):
175
address = Faker('address')
176
177
# Custom provider example
178
from faker.providers import BaseProvider
179
180
class CustomProvider(BaseProvider):
181
def department_code(self):
182
return self.random_element(['ENG', 'SAL', 'MKT', 'HR'])
183
184
Faker.add_provider(CustomProvider)
185
186
class EmployeeFactory(Factory):
187
dept_code = Faker('department_code')
188
```
189
190
### Container and Complex Declarations
191
192
Declarations for working with containers and accessing factory context.
193
194
```python { .api }
195
class ContainerAttribute:
196
"""
197
Receives current instance and container chain for complex attribute computation.
198
199
Args:
200
function: Callable that takes (instance, containers) and returns value
201
strict (bool): Whether to enforce strict container access
202
"""
203
def __init__(self, function, strict=True): ...
204
```
205
206
#### Usage Examples
207
208
```python
209
class ProfileFactory(Factory):
210
class Meta:
211
model = Profile
212
213
# Access container chain (useful in SubFactory contexts)
214
user_id = ContainerAttribute(lambda obj, containers: containers[0].id if containers else None)
215
216
# Complex container logic
217
role = ContainerAttribute(
218
lambda obj, containers: 'admin' if containers and containers[0].is_staff else 'user'
219
)
220
```
221
222
### Factory Composition
223
224
Declarations for creating related objects and complex data structures.
225
226
```python { .api }
227
class SubFactory:
228
"""
229
Create related objects using another factory.
230
231
Args:
232
factory: Factory class or string path to factory
233
**defaults: Default values to pass to the sub-factory
234
"""
235
def __init__(self, factory, **defaults): ...
236
237
def get_factory(self):
238
"""Retrieve the wrapped factory class."""
239
240
class Dict:
241
"""
242
Fill dictionary with declarations.
243
244
Args:
245
params (dict): Dictionary of key-value pairs, values can be declarations
246
dict_factory (str): Factory to use for creating dictionary
247
"""
248
def __init__(self, params, dict_factory='factory.DictFactory'): ...
249
250
class List:
251
"""
252
Fill list with declarations.
253
254
Args:
255
params (list): List of values, can include declarations
256
list_factory (str): Factory to use for creating list
257
"""
258
def __init__(self, params, list_factory='factory.ListFactory'): ...
259
```
260
261
#### Usage Examples
262
263
```python
264
class UserFactory(Factory):
265
class Meta:
266
model = User
267
268
name = Faker('name')
269
email = Faker('email')
270
271
class PostFactory(Factory):
272
class Meta:
273
model = Post
274
275
title = Faker('sentence')
276
277
# SubFactory creates related User
278
author = SubFactory(UserFactory, name='Post Author')
279
280
# Dict with mixed static and dynamic values
281
metadata = Dict({
282
'views': 0,
283
'created_at': LazyFunction(lambda: timezone.now().isoformat()),
284
'tags': List([Faker('word'), Faker('word'), 'default-tag'])
285
})
286
287
# List of related objects
288
comments = List([
289
SubFactory('CommentFactory', content='First comment'),
290
SubFactory('CommentFactory', content='Second comment')
291
])
292
```
293
294
### Conditional Logic
295
296
Declarations for conditional attribute generation based on other values.
297
298
```python { .api }
299
class Maybe:
300
"""
301
Conditional declaration based on decider value.
302
303
Args:
304
decider (str or callable): Field name or function to evaluate condition
305
yes_declaration: Declaration to use when condition is truthy
306
no_declaration: Declaration to use when condition is falsy (defaults to SKIP)
307
"""
308
def __init__(self, decider, yes_declaration=SKIP, no_declaration=SKIP): ...
309
310
class Trait:
311
"""
312
Enable declarations based on boolean flag, used in Params section.
313
314
Args:
315
**overrides: Attribute overrides to apply when trait is enabled
316
"""
317
def __init__(self, **overrides): ...
318
```
319
320
#### Usage Examples
321
322
```python
323
class UserFactory(Factory):
324
class Meta:
325
model = User
326
327
name = Faker('name')
328
is_premium = Iterator([True, False])
329
330
# Maybe based on field value
331
premium_features = Maybe(
332
'is_premium',
333
yes_declaration=Dict({'advanced_analytics': True, 'priority_support': True}),
334
no_declaration=Dict({'basic_features': True})
335
)
336
337
# Maybe with function decider
338
account_type = Maybe(
339
lambda obj: obj.is_premium,
340
yes_declaration='Premium',
341
no_declaration='Basic'
342
)
343
344
class PostFactory(Factory):
345
class Meta:
346
model = Post
347
348
title = Faker('sentence')
349
status = 'draft'
350
351
class Params:
352
# Trait for published posts
353
published = Trait(
354
status='published',
355
published_at=LazyFunction(lambda: timezone.now()),
356
slug=LazyAttribute(lambda obj: slugify(obj.title))
357
)
358
359
# Usage: PostFactory(published=True) enables the published trait
360
published_post = PostFactory(published=True)
361
```
362
363
### Post-Generation Declarations
364
365
Declarations that execute after object creation for relationships and method calls.
366
367
```python { .api }
368
class PostGeneration:
369
"""
370
Call function after object generation.
371
372
Args:
373
function: Callable that takes (instance, create, extracted, **kwargs)
374
"""
375
def __init__(self, function): ...
376
377
class PostGenerationMethodCall:
378
"""
379
Call method on generated object after creation.
380
381
Args:
382
method_name (str): Name of method to call on the instance
383
*args: Positional arguments to pass to method
384
**kwargs: Keyword arguments to pass to method
385
"""
386
def __init__(self, method_name, *args, **kwargs): ...
387
388
class RelatedFactory:
389
"""
390
Create related object after main object generation.
391
392
Args:
393
factory: Factory class to use for creating related object
394
factory_related_name (str): Attribute name on related object to set main object
395
**defaults: Default values for related object factory
396
"""
397
def __init__(self, factory, factory_related_name='', **defaults): ...
398
399
class RelatedFactoryList:
400
"""
401
Create multiple related objects after main object generation.
402
403
Args:
404
factory: Factory class to use for creating related objects
405
factory_related_name (str): Attribute name on related objects to set main object
406
size (int): Number of related objects to create
407
**defaults: Default values for related object factory
408
"""
409
def __init__(self, factory, factory_related_name='', size=2, **defaults): ...
410
```
411
412
#### Usage Examples
413
414
```python
415
class UserFactory(Factory):
416
class Meta:
417
model = User
418
419
username = Faker('user_name')
420
email = Faker('email')
421
422
# PostGeneration for custom setup
423
setup_profile = PostGeneration(
424
lambda obj, create, extracted, **kwargs: obj.create_default_profile() if create else None
425
)
426
427
# PostGenerationMethodCall for method execution
428
set_password = PostGenerationMethodCall('set_password', 'default_password')
429
430
# RelatedFactory creates profile after user
431
profile = RelatedFactory('ProfileFactory', 'user')
432
433
class PostFactory(Factory):
434
class Meta:
435
model = Post
436
437
title = Faker('sentence')
438
author = SubFactory(UserFactory)
439
440
# RelatedFactoryList creates multiple comments
441
comments = RelatedFactoryList(
442
'CommentFactory',
443
'post',
444
size=3,
445
author=SubFactory(UserFactory)
446
)
447
448
# Usage with extracted values
449
user = UserFactory(set_password__password='custom_password')
450
```
451
452
## Special Values and Constants
453
454
```python { .api }
455
# Special marker for skipping fields
456
SKIP = object()
457
458
# Unspecified default marker
459
UNSPECIFIED = object()
460
```
461
462
## Declaration Wrapper Functions
463
464
Functional-style wrappers for creating declarations:
465
466
```python { .api }
467
def lazy_attribute(func):
468
"""Wrap function as LazyAttribute declaration."""
469
470
def sequence(func):
471
"""Wrap function as Sequence declaration."""
472
473
def lazy_attribute_sequence(func):
474
"""Wrap function as LazyAttributeSequence declaration."""
475
476
def container_attribute(func):
477
"""Wrap function as ContainerAttribute declaration (non-strict)."""
478
479
def post_generation(func):
480
"""Wrap function as PostGeneration declaration."""
481
```
482
483
#### Usage Examples
484
485
```python
486
# Using wrapper functions
487
@lazy_attribute
488
def full_name(obj):
489
return f'{obj.first_name} {obj.last_name}'
490
491
@sequence
492
def email(n):
493
return f'user{n}@example.com'
494
495
@post_generation
496
def send_welcome_email(obj, create, extracted, **kwargs):
497
if create:
498
obj.send_welcome_email()
499
500
class UserFactory(Factory):
501
class Meta:
502
model = User
503
504
first_name = Faker('first_name')
505
last_name = Faker('last_name')
506
full_name = full_name
507
email = email
508
send_welcome_email = send_welcome_email
509
```