diff options
Diffstat (limited to 'docs/reference.rst')
-rw-r--r-- | docs/reference.rst | 185 |
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' |