summaryrefslogtreecommitdiff
path: root/docs/reference.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/reference.rst')
-rw-r--r--docs/reference.rst185
1 files changed, 175 insertions, 10 deletions
diff --git a/docs/reference.rst b/docs/reference.rst
index 81aa645..53584a0 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -19,15 +19,18 @@ The :class:`Factory` class
.. attribute:: FACTORY_FOR
- This required attribute describes the class of objects to generate.
- It may only be absent if the factory has been marked abstract through
- :attr:`ABSTRACT_FACTORY`.
+ This optional attribute describes the class of objects to generate.
+
+ If unset, it will be inherited from parent :class:`Factory` subclasses.
.. attribute:: ABSTRACT_FACTORY
This attribute indicates that the :class:`Factory` subclass should not
be used to generate objects, but instead provides some extra defaults.
+ It will be automatically set to ``True`` if neither the :class:`Factory`
+ subclass nor its parents define the :attr:`~Factory.FACTORY_FOR` attribute.
+
.. attribute:: FACTORY_ARG_PARAMETERS
Some factories require non-keyword arguments to their :meth:`~object.__init__`.
@@ -211,7 +214,7 @@ The :class:`Factory` class
.. code-block:: python
class BaseBackendFactory(factory.Factory):
- ABSTRACT_FACTORY = True
+ ABSTRACT_FACTORY = True # Optional
def _create(cls, target_class, *args, **kwargs):
obj = target_class(*args, **kwargs)
@@ -234,6 +237,48 @@ The :class:`Factory` class
values, for instance.
+ **Advanced functions:**
+
+
+ .. classmethod:: reset_sequence(cls, value=None, force=False)
+
+ :arg int value: The value to reset the sequence to
+ :arg bool force: Whether to force-reset the sequence
+
+ Allows to reset the sequence counter for a :class:`~factory.Factory`.
+ The new value can be passed in as the ``value`` argument:
+
+ .. code-block:: pycon
+
+ >>> SomeFactory.reset_sequence(4)
+ >>> SomeFactory._next_sequence
+ 4
+
+ Since subclasses of a non-:attr:`abstract <factory.Factory.ABSTRACT_FACTORY>`
+ :class:`~factory.Factory` share the same sequence counter, special care needs
+ to be taken when resetting the counter of such a subclass.
+
+ By default, :meth:`reset_sequence` will raise a :exc:`ValueError` when
+ called on a subclassed :class:`~factory.Factory` subclass. This can be
+ avoided by passing in the ``force=True`` flag:
+
+ .. code-block:: pycon
+
+ >>> InheritedFactory.reset_sequence()
+ Traceback (most recent call last):
+ File "factory_boy/tests/test_base.py", line 179, in test_reset_sequence_subclass_parent
+ SubTestObjectFactory.reset_sequence()
+ File "factory_boy/factory/base.py", line 250, in reset_sequence
+ "Cannot reset the sequence of a factory subclass. "
+ ValueError: Cannot reset the sequence of a factory subclass. Please call reset_sequence() on the root factory, or call reset_sequence(forward=True).
+
+ >>> InheritedFactory.reset_sequence(force=True)
+ >>>
+
+ This is equivalent to calling :meth:`reset_sequence` on the base
+ factory in the chain.
+
+
.. _strategies:
Strategies
@@ -318,6 +363,38 @@ factory_boy supports two main strategies for generating instances, plus stubs.
with a default strategy set to :data:`STUB_STRATEGY`.
+.. function:: debug(logger='factory', stream=None)
+
+ :param str logger: The name of the logger to enable debug for
+ :param file stream: The stream to send debug output to, defaults to :obj:`sys.stderr`
+
+ Context manager to help debugging factory_boy behavior.
+ It will temporarily put the target logger (e.g ``'factory'``) in debug mode,
+ sending all output to :obj`~sys.stderr`;
+ upon leaving the context, the logging levels are reset.
+
+ A typical use case is to understand what happens during a single factory call:
+
+ .. code-block:: python
+
+ with factory.debug():
+ obj = TestModel2Factory()
+
+ This will yield messages similar to those (artificial indentation):
+
+ .. code-block:: ini
+
+ BaseFactory: Preparing tests.test_using.TestModel2Factory(extra={})
+ LazyStub: Computing values for tests.test_using.TestModel2Factory(two=<OrderedDeclarationWrapper for <factory.declarations.SubFactory object at 0x1e15610>>)
+ SubFactory: Instantiating tests.test_using.TestModelFactory(__containers=(<LazyStub for tests.test_using.TestModel2Factory>,), one=4), create=True
+ BaseFactory: Preparing tests.test_using.TestModelFactory(extra={'__containers': (<LazyStub for tests.test_using.TestModel2Factory>,), 'one': 4})
+ LazyStub: Computing values for tests.test_using.TestModelFactory(one=4)
+ LazyStub: Computed values, got tests.test_using.TestModelFactory(one=4)
+ BaseFactory: Generating tests.test_using.TestModelFactory(one=4)
+ LazyStub: Computed values, got tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>)
+ BaseFactory: Generating tests.test_using.TestModel2Factory(two=<tests.test_using.TestModel object at 0x1e15410>)
+
+
.. _declarations:
Declarations
@@ -353,6 +430,11 @@ accept the object being built as sole argument, and return a value.
'leo@example.com'
+The object passed to :class:`LazyAttribute` is not an instance of the target class,
+but instead a :class:`~containers.LazyStub`: a temporary container that computes
+the value of all declared fields.
+
+
Decorator
~~~~~~~~~
@@ -586,7 +668,7 @@ handles more complex cases:
SubFactory
""""""""""
-.. class:: SubFactory(sub_factory, **kwargs)
+.. class:: SubFactory(factory, **kwargs)
.. OHAI_VIM**
@@ -601,6 +683,20 @@ The :class:`SubFactory` attribute should be called with:
factory
+.. note::
+
+ When passing an actual :class:`~factory.Factory` for the
+ :attr:`~factory.SubFactory.factory` argument, make sure to pass
+ the class and not instance (i.e no ``()`` after the class):
+
+ .. code-block:: python
+
+ class FooFactory(factory.Factory):
+ FACTORY_FOR = Foo
+
+ bar = factory.SubFactory(BarFactory) # Not BarFactory()
+
+
Definition
~~~~~~~~~~
@@ -786,6 +882,19 @@ Obviously, this "follow parents" hability also handles overriding some attribute
'cn'
+This feature is also available to :class:`LazyAttribute` and :class:`LazyAttributeSequence`,
+through the :attr:`~containers.LazyStub.factory_parent` attribute of the passed-in object:
+
+.. code-block:: python
+
+ class CompanyFactory(factory.Factory):
+ FACTORY_FOR = Company
+ country = factory.SubFactory(CountryFactory)
+ owner = factory.SubFactory(UserFactory,
+ language=factory.LazyAttribute(lambda user: user.factory_parent.country.language),
+ )
+
+
Iterator
""""""""
@@ -810,6 +919,14 @@ Iterator
.. versionadded:: 1.3.0
+ .. method:: reset()
+
+ Reset the internal iterator used by the attribute, so that the next value
+ will be the first value generated by the iterator.
+
+ May be called several times.
+
+
Each call to the factory will receive the next value from the iterable:
.. code-block:: python
@@ -879,6 +996,24 @@ use the :func:`iterator` decorator:
yield line
+Resetting
+~~~~~~~~~
+
+In order to start back at the first value in an :class:`Iterator`,
+simply call the :meth:`~Iterator.reset` method of that attribute
+(accessing it from the bare :class:`~Factory` subclass):
+
+.. code-block:: pycon
+
+ >>> UserFactory().lang
+ 'en'
+ >>> UserFactory().lang
+ 'fr'
+ >>> UserFactory.lang.reset()
+ >>> UserFactory().lang
+ 'en'
+
+
Dict and List
"""""""""""""
@@ -1013,7 +1148,7 @@ as keyword arguments; ``{'post_x': 2}`` will be passed to ``SomeFactory.FACTORY_
RelatedFactory
""""""""""""""
-.. class:: RelatedFactory(factory, name='', **kwargs)
+.. class:: RelatedFactory(factory, factory_related_name='', **kwargs)
.. OHAI_VIM**
@@ -1033,13 +1168,27 @@ RelatedFactory
.. attribute:: name
The generated object (where the :class:`RelatedFactory` attribute will
- set) may be passed to the related factory if the :attr:`name` parameter
+ set) may be passed to the related factory if the :attr:`factory_related_name` parameter
is set.
It will be passed as a keyword argument, using the :attr:`name` value as
keyword:
+.. note::
+
+ When passing an actual :class:`~factory.Factory` for the
+ :attr:`~factory.RelatedFactory.factory` argument, make sure to pass
+ the class and not instance (i.e no ``()`` after the class):
+
+ .. code-block:: python
+
+ class FooFactory(factory.Factory):
+ FACTORY_FOR = Foo
+
+ bar = factory.RelatedFactory(BarFactory) # Not BarFactory()
+
+
.. code-block:: python
class CityFactory(factory.Factory):
@@ -1069,6 +1218,22 @@ Extra kwargs may be passed to the related factory, through the usual ``ATTR__SUB
>>> City.objects.get(capital_of=england)
<City: London>
+If a value if passed for the :class:`RelatedFactory` attribute, this disables
+:class:`RelatedFactory` generation:
+
+.. code-block:: pycon
+
+ >>> france = CountryFactory()
+ >>> paris = City.objects.get()
+ >>> paris
+ <City: Paris>
+ >>> reunion = CountryFactory(capital_city=paris)
+ >>> City.objects.count() # No new capital_city generated
+ 1
+ >>> guyane = CountryFactory(capital_city=paris, capital_city__name='Kourou')
+ >>> City.objects.count() # No new capital_city generated, ``name`` ignored.
+ 1
+
PostGeneration
""""""""""""""
@@ -1224,7 +1389,7 @@ factory during instantiation.
.. code-block:: python
- class UserFactory(factory.DjangoModelFactory):
+ class UserFactory(factory.django.DjangoModelFactory):
FACTORY_FOR = User
username = 'user'
@@ -1315,12 +1480,12 @@ Lightweight factory declaration
UserFactory = make_factory(models.User,
login='john',
email=factory.LazyAttribute(lambda u: '%s@example.com' % u.login),
- FACTORY_CLASS=factory.DjangoModelFactory,
+ FACTORY_CLASS=factory.django.DjangoModelFactory,
)
# This is equivalent to:
- class UserFactory(factory.DjangoModelFactory):
+ class UserFactory(factory.django.DjangoModelFactory):
FACTORY_FOR = models.User
login = 'john'