summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/changelog.rst7
-rw-r--r--docs/conf.py10
-rw-r--r--docs/examples.rst28
-rw-r--r--docs/ideas.rst11
-rw-r--r--docs/internals.rst2
-rw-r--r--docs/introduction.rst258
-rw-r--r--docs/orms.rst36
-rw-r--r--docs/post_generation.rst91
-rw-r--r--docs/recipes.rst62
-rw-r--r--docs/reference.rst1027
-rw-r--r--docs/subfactory.rst81
11 files changed, 1432 insertions, 181 deletions
diff --git a/docs/changelog.rst b/docs/changelog.rst
index c97f735..2e7aaea 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -157,4 +157,11 @@ The following features have been deprecated and will be removed in an upcoming r
- First version of factory_boy
+Credits
+-------
+
+* Initial version by Mark Sandstrom (2010)
+* Developed by Raphaël Barrois since 2011
+
+
.. vim:et:ts=4:sw=4:tw=119:ft=rst:
diff --git a/docs/conf.py b/docs/conf.py
index fd9ded6..0ccaf29 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -42,7 +42,7 @@ master_doc = 'index'
# General information about the project.
project = u'Factory Boy'
-copyright = u'2011, Raphaël Barrois, Mark Sandstrom'
+copyright = u'2011-2013, Raphaël Barrois, Mark Sandstrom'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -231,4 +231,10 @@ man_pages = [
# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {'http://docs.python.org/': None}
+intersphinx_mapping = {
+ 'http://docs.python.org/': None,
+ 'django': (
+ 'http://docs.djangoproject.com/en/dev/',
+ 'http://docs.djangoproject.com/en/dev/_objects/',
+ ),
+}
diff --git a/docs/examples.rst b/docs/examples.rst
index cac6bc6..aab990a 100644
--- a/docs/examples.rst
+++ b/docs/examples.rst
@@ -7,7 +7,10 @@ Here are some real-world examples of using FactoryBoy.
Objects
-------
-First, let's define a couple of objects::
+First, let's define a couple of objects:
+
+
+.. code-block:: python
class Account(object):
def __init__(self, username, email):
@@ -41,7 +44,10 @@ First, let's define a couple of objects::
Factories
---------
-And now, we'll define the related factories::
+And now, we'll define the related factories:
+
+
+.. code-block:: python
import factory
import random
@@ -60,15 +66,18 @@ And now, we'll define the related factories::
FACTORY_FOR = objects.Profile
account = factory.SubFactory(AccountFactory)
- gender = random.choice([objects.Profile.GENDER_MALE, objects.Profile.GENDER_FEMALE])
+ gender = factory.Iterator([objects.Profile.GENDER_MALE, objects.Profile.GENDER_FEMALE])
firstname = u'John'
lastname = u'Doe'
-We have now defined basic factories for our :py:class:`~Account` and :py:class:`~Profile` classes.
+We have now defined basic factories for our :class:`~Account` and :class:`~Profile` classes.
+
+If we commonly use a specific variant of our objects, we can refine a factory accordingly:
-If we commonly use a specific variant of our objects, we can refine a factory accordingly::
+
+.. code-block:: python
class FemaleProfileFactory(ProfileFactory):
gender = objects.Profile.GENDER_FEMALE
@@ -80,7 +89,10 @@ If we commonly use a specific variant of our objects, we can refine a factory ac
Using the factories
-------------------
-We can now use our factories, for tests::
+We can now use our factories, for tests:
+
+
+.. code-block:: python
import unittest
@@ -112,7 +124,9 @@ We can now use our factories, for tests::
self.assertLess(stats.genders[objects.Profile.GENDER_FEMALE], 2)
-Or for fixtures::
+Or for fixtures:
+
+.. code-block:: python
from . import factories
diff --git a/docs/ideas.rst b/docs/ideas.rst
new file mode 100644
index 0000000..004f722
--- /dev/null
+++ b/docs/ideas.rst
@@ -0,0 +1,11 @@
+Ideas
+=====
+
+
+This is a list of future features that may be incorporated into factory_boy:
+
+* **A 'options' attribute**: instead of adding more class-level constants, use a django-style ``class Meta`` Factory attribute with all options there
+* **factory-local fields**: Allow some fields to be available while building attributes,
+ but not passed to the associated class.
+ For instance, a ``global_kind`` field would be used to select values for many other fields.
+
diff --git a/docs/internals.rst b/docs/internals.rst
new file mode 100644
index 0000000..a7402ff
--- /dev/null
+++ b/docs/internals.rst
@@ -0,0 +1,2 @@
+Internals
+=========
diff --git a/docs/introduction.rst b/docs/introduction.rst
new file mode 100644
index 0000000..d211a83
--- /dev/null
+++ b/docs/introduction.rst
@@ -0,0 +1,258 @@
+Introduction
+============
+
+
+The purpose of factory_boy is to provide a default way of getting a new instance,
+while still being able to override some fields on a per-call basis.
+
+
+.. note:: This section will drive you through an overview of factory_boy's feature.
+ New users are advised to spend a few minutes browsing through this list
+ of useful helpers.
+
+ Users looking for quick helpers may take a look at :doc:`recipes`,
+ while those needing detailed documentation will be interested in the :doc:`reference` section.
+
+
+Basic usage
+-----------
+
+
+Factories declare a set of attributes used to instantiate an object, whose class is defined in the FACTORY_FOR attribute:
+
+- Subclass ``factory.Factory`` (or a more suitable subclass)
+- Set its ``FACTORY_FOR`` attribute to the target class
+- Add defaults for keyword args to pass to the associated class' ``__init__`` method
+
+
+.. code-block:: python
+
+ import factory
+ from . import base
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = base.User
+
+ firstname = "John"
+ lastname = "Doe"
+
+You may now get ``base.User`` instances trivially:
+
+.. code-block:: pycon
+
+ >>> john = UserFactory()
+ <User: John Doe>
+
+It is also possible to override the defined attributes by passing keyword arguments to the factory:
+
+.. code-block:: pycon
+
+ >>> jack = UserFactory(firstname="Jack")
+ <User: Jack Doe>
+
+
+A given class may be associated to many :class:`~factory.Factory` subclasses:
+
+.. code-block:: python
+
+ class EnglishUserFactory(factory.Factory):
+ FACTORY_FOR = base.User
+
+ firstname = "John"
+ lastname = "Doe"
+ lang = 'en'
+
+
+ class FrenchUserFactory(factory.Factory):
+ FACTORY_FOR = base.User
+
+ firstname = "Jean"
+ lastname = "Dupont"
+ lang = 'fr'
+
+
+.. code-block:: pycon
+
+ >>> EnglishUserFactory()
+ <User: John Doe (en)>
+ >>> FrenchUserFactory()
+ <User: Jean Dupont (fr)>
+
+
+Sequences
+---------
+
+When a field has a unique key, each object generated by the factory should have a different value for that field.
+This is achieved with the :class:`~factory.Sequence` declaration:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = models.User
+
+ username = factory.Sequence(lambda n: 'user%d' % n)
+
+.. code-block:: pycon
+
+ >>> UserFactory()
+ <User: user1>
+ >>> UserFactory()
+ <User: user2>
+
+.. note:: For more complex situations, you may also use the :meth:`~factory.@sequence` decorator:
+
+ .. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = models.User
+
+ @factory.sequence
+ def username(self, n):
+ return 'user%d' % n
+
+
+LazyAttribute
+-------------
+
+Some fields may be deduced from others, for instance the email based on the username.
+The :class:`~factory.LazyAttribute` handles such cases: it should receive a function
+taking the object being built and returning the value for the field:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = models.User
+
+ username = factory.Sequence(lambda n: 'user%d' % n)
+ email = factory.LazyAttribute(lambda obj: '%s@example.com' % obj.username)
+
+.. code-block:: pycon
+
+ >>> UserFactory()
+ <User: user1 (user1@example.com)>
+
+ >>> # The LazyAttribute handles overridden fields
+ >>> UserFactory(username='john')
+ <User: john (john@example.com)>
+
+ >>> # They can be directly overridden as well
+ >>> UserFactory(email='doe@example.com')
+ <User: user3 (doe@example.com)>
+
+
+.. note:: As for :class:`~factory.Sequence`, a :meth:`~factory.@lazy_attribute` decorator is available.
+
+
+Inheritance
+-----------
+
+
+Once a "base" factory has been defined for a given class,
+alternate versions can be easily defined through subclassing.
+
+The subclassed :class:`~factory.Factory` will inherit all declarations from its parent,
+and update them with its own declarations:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = base.User
+
+ firstname = "John"
+ lastname = "Doe"
+ group = 'users'
+
+ class AdminFactory(UserFactory):
+ admin = True
+ group = 'admins'
+
+.. code-block:: pycon
+
+ >>> user = UserFactory()
+ >>> user
+ <User: John Doe>
+ >>> user.group
+ 'users'
+
+ >>> admin = AdminFactory()
+ >>> admin
+ <User: John Doe (admin)>
+ >>> admin.group # The AdminFactory field has overridden the base field
+ 'admins'
+
+
+Any argument of all factories in the chain can easily be overridden:
+
+.. code-block:: pycon
+
+ >>> super_admin = AdminFactory(group='superadmins', lastname="Lennon")
+ >>> super_admin
+ <User: John Lennon (admin)>
+ >>> super_admin.group # Overridden at call time
+ 'superadmins'
+
+
+Non-kwarg arguments
+-------------------
+
+Some classes take a few, non-kwarg arguments first.
+
+This is handled by the :data:`~factory.Factory.FACTORY_ARG_PARAMETERS` attribute:
+
+.. code-block:: python
+
+ class MyFactory(factory.Factory):
+ FACTORY_FOR = MyClass
+ FACTORY_ARG_PARAMETERS = ('x', 'y')
+
+ x = 1
+ y = 2
+ z = 3
+
+.. code-block:: pycon
+
+ >>> MyFactory(y=4)
+ <MyClass(1, 4, z=3)>
+
+
+Strategies
+----------
+
+All factories support two built-in strategies:
+
+* ``build`` provides a local object
+* ``create`` instantiates a local object, and saves it to the database.
+
+.. note:: For 1.X versions, the ``create`` will actually call ``AssociatedClass.objects.create``,
+ as for a Django model.
+
+ Starting from 2.0, :meth:`factory.Factory.create` simply calls ``AssociatedClass(**kwargs)``.
+ You should use :class:`~factory.DjangoModelFactory` for Django models.
+
+
+When a :class:`~factory.Factory` includes related fields (:class:`~factory.SubFactory`, :class:`~factory.RelatedFactory`),
+the parent's strategy will be pushed onto related factories.
+
+
+Calling a :class:`~factory.Factory` subclass will provide an object through the default strategy:
+
+.. code-block:: python
+
+ class MyFactory(factory.Factory):
+ FACTORY_FOR = MyClass
+
+.. code-block:: pycon
+
+ >>> MyFactory.create()
+ <MyFactory: X (saved)>
+
+ >>> MyFactory.build()
+ <MyFactory: X (unsaved)>
+
+ >>> MyFactory() # equivalent to MyFactory.create()
+ <MyClass: X (saved)>
+
+
+The default strategy can ba changed by setting the class-level :attr:`~factory.Factory.default_strategy` attribute.
+
+
diff --git a/docs/orms.rst b/docs/orms.rst
new file mode 100644
index 0000000..eae31d9
--- /dev/null
+++ b/docs/orms.rst
@@ -0,0 +1,36 @@
+Using factory_boy with ORMs
+===========================
+
+.. currentmodule:: factory
+
+
+factory_boy provides custom :class:`Factory` subclasses for various ORMs,
+adding dedicated features.
+
+
+Django
+------
+
+
+The first versions of factory_boy were designed specifically for Django,
+but the library has now evolved to be framework-independant.
+
+Most features should thus feel quite familiar to Django users.
+
+The :class:`DjangoModelFactory` subclass
+"""""""""""""""""""""""""""""""""""""""""
+
+All factories for a Django :class:`~django.db.models.Model` should use the
+:class:`DjangoModelFactory` base class.
+
+
+.. class:: DjangoModelFactory(Factory)
+
+ Dedicated class for Django :class:`~django.db.models.Model` factories.
+
+ This class provides the following features:
+
+ * :func:`~Factory.create()` uses :meth:`Model.objects.create() <django.db.models.query.QuerySet.create>`
+ * :func:`~Factory._setup_next_sequence()` selects the next unused primary key value
+ * When using :class:`~factory.RelatedFactory` attributes, the base object will be
+ :meth:`saved <django.db.models.Model.save>` once all post-generation hooks have run.
diff --git a/docs/post_generation.rst b/docs/post_generation.rst
deleted file mode 100644
index d712abc..0000000
--- a/docs/post_generation.rst
+++ /dev/null
@@ -1,91 +0,0 @@
-PostGenerationHook
-==================
-
-
-Some objects expect additional method calls or complex processing for proper definition.
-For instance, a ``User`` may need to have a related ``Profile``, where the ``Profile`` is built from the ``User`` object.
-
-To support this pattern, factory_boy provides the following tools:
- - :py:class:`factory.PostGeneration`: this class allows calling a given function with the generated object as argument
- - :py:func:`factory.post_generation`: decorator performing the same functions as :py:class:`~factory.PostGeneration`
- - :py:class:`factory.RelatedFactory`: this builds or creates a given factory *after* building/creating the first Factory.
-
-
-Passing arguments to a post-generation hook
--------------------------------------------
-
-A post-generation hook will be defined with a given attribute name.
-When calling the ``Factory``, some arguments will be passed to the post-generation hook instead of being available for ``Factory`` building:
-
- - An argument with the same name as the post-generation hook attribute will be passed to the hook
- - All arguments beginning with that name and ``__`` will be passed to the hook, after removing the prefix.
-
-Example::
-
- class MyFactory(factory.Factory):
- blah = factory.PostGeneration(lambda obj, create, extracted, **kwargs: 42)
-
- MyFactory(
- blah=42, # Passed in the 'extracted' argument of the lambda
- blah__foo=1, # Passed in kwargs as 'foo': 1
- blah__baz=2, # Passed in kwargs as 'baz': 2
- blah_bar=3, # Not passed
- )
-
-The prefix used for extraction can be changed by setting the ``extract_prefix`` argument of the hook::
-
- class MyFactory(factory.Factory):
- @factory.post_generation(extract_prefix='bar')
- def foo(self, create, extracted, **kwargs):
- self.foo = extracted
-
- MyFactory(
- bar=42, # Will be passed to 'extracted'
- bar__baz=13, # Will be passed as 'baz': 13 in kwargs
- foo=2, # Won't be passed to the post-generation hook
- )
-
-
-PostGeneration and @post_generation
------------------------------------
-
-Both declarations wrap a function, which will be called with the following arguments:
- - ``obj``: the generated factory
- - ``create``: whether the factory was "built" or "created"
- - ``extracted``: if the :py:class:`~factory.PostGeneration` was declared as attribute ``foo``,
- and another value was given for ``foo`` when calling the ``Factory``,
- that value will be available in the ``extracted`` parameter.
- - other keyword args are extracted from those passed to the ``Factory`` with the same prefix as the name of the :py:class:`~factory.PostGeneration` attribute
-
-
-RelatedFactory
---------------
-
-This is declared with the following arguments:
- - ``factory``: the :py:class:`~factory.Factory` to call
- - ``name``: the keyword to use when passing this object to the related :py:class:`~factory.Factory`; if empty, the object won't be passed to the related :py:class:`~factory.Factory`
- - Extra keyword args which will be passed to the factory
-
-When the object is built, the keyword arguments passed to the related :py:class:`~factory.Factory` are:
- - ``name: obj`` if ``name`` was passed when defining the :py:class:`~factory.RelatedFactory`
- - extra keyword args defined in the :py:class:`~factory.RelatedFactory` definition, overridden by any prefixed arguments passed to the object definition
-
-
-Example::
-
- class RelatedObjectFactory(factory.Factory):
- FACTORY_FOR = RelatedObject
- one = 1
- two = 2
- related = None
-
- class ObjectWithRelatedFactory(factory.Factory):
- FACTORY_FOR = SomeObject
- foo = factory.RelatedFactory(RelatedObjectFactory, 'related', one=2)
-
- ObjectWithRelatedFactory(foo__two=3)
-
-The ``RelatedObject`` will be called with:
- - ``one=2``
- - ``two=3``
- - ``related=<SomeObject>``
diff --git a/docs/recipes.rst b/docs/recipes.rst
new file mode 100644
index 0000000..8af0c8f
--- /dev/null
+++ b/docs/recipes.rst
@@ -0,0 +1,62 @@
+Common recipes
+==============
+
+
+.. note:: Most recipes below take on Django model examples, but can also be used on their own.
+
+
+Dependent objects (ForeignKey)
+------------------------------
+
+When one attribute is actually a complex field
+(e.g a :class:`~django.db.models.ForeignKey` to another :class:`~django.db.models.Model`),
+use the :class:`~factory.SubFactory` declaration:
+
+
+.. code-block:: python
+
+ # models.py
+ class User(models.Model):
+ first_name = models.CharField()
+ group = models.ForeignKey(Group)
+
+
+ # factories.py
+ import factory
+ from . import models
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = models.User
+
+ first_name = factory.Sequence(lambda n: "Agent %03d" % n)
+ group = factory.SubFactory(GroupFactory)
+
+
+Reverse dependencies (reverse ForeignKey)
+-----------------------------------------
+
+When a related object should be created upon object creation
+(e.g a reverse :class:`~django.db.models.ForeignKey` from another :class:`~django.db.models.Model`),
+use a :class:`~factory.RelatedFactory` declaration:
+
+
+.. code-block:: python
+
+ # models.py
+ class User(models.Model):
+ pass
+
+ class UserLog(models.Model):
+ user = models.ForeignKey(User)
+ action = models.CharField()
+
+
+ # factories.py
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = models.User
+
+ log = factory.RelatedFactory(UserLogFactory, 'user', action=models.UserLog.ACTION_CREATE)
+
+
+When a :class:`UserFactory` is instantiated, factory_boy will call
+``UserLogFactory(user=that_user, action=...)`` just before returning the created ``User``.
diff --git a/docs/reference.rst b/docs/reference.rst
new file mode 100644
index 0000000..289a9a8
--- /dev/null
+++ b/docs/reference.rst
@@ -0,0 +1,1027 @@
+Reference
+=========
+
+.. currentmodule:: factory
+
+This section offers an in-depth description of factory_boy features.
+
+For internals and customization points, please refer to the :doc:`internals` section.
+
+
+The :class:`Factory` class
+--------------------------
+
+.. class:: Factory
+
+ The :class:`Factory` class is the base of factory_boy features.
+
+ It accepts a few specific attributes (must be specified on class declaration):
+
+ .. 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`.
+
+ .. attribute:: ABSTRACT_FACTORY
+
+ This attribute indicates that the :class:`Factory` subclass should not
+ be used to generate objects, but instead provides some extra defaults.
+
+ .. attribute:: FACTORY_ARG_PARAMETERS
+
+ Some factories require non-keyword arguments to their :meth:`~object.__init__`.
+ They should be listed, in order, in the :attr:`FACTORY_ARG_PARAMETERS`
+ attribute:
+
+ .. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+ FACTORY_ARG_PARAMETERS = ('login', 'email')
+
+ login = 'john'
+ email = factory.LazyAttribute(lambda o: '%s@example.com' % o.login)
+ firstname = "John"
+
+ .. code-block:: pycon
+
+ >>> UserFactory()
+ <User: john>
+ >>> User('john', 'john@example.com', firstname="John") # actual call
+
+ **Base functions:**
+
+ The :class:`Factory` class provides a few methods for getting objects;
+ the usual way being to simply call the class:
+
+ .. code-block:: pycon
+
+ >>> UserFactory() # Calls UserFactory.create()
+ >>> UserFactory(login='john') # Calls UserFactory.create(login='john')
+
+ Under the hood, factory_boy will define the :class:`Factory`
+ :meth:`~object.__new__` method to call the default :ref:`strategy <strategies>`
+ of the :class:`Factory`.
+
+
+ A specific strategy for getting instance can be selected by calling the
+ adequate method:
+
+ .. classmethod:: build(cls, **kwargs)
+
+ Provides a new object, using the 'build' strategy.
+
+ .. classmethod:: build_batch(cls, size, **kwargs)
+
+ Provides a list of :obj:`size` instances from the :class:`Factory`,
+ through the 'build' strategy.
+
+
+ .. classmethod:: create(cls, **kwargs)
+
+ Provides a new object, using the 'create' strategy.
+
+ .. classmethod:: create_batch(cls, size, **kwargs)
+
+ Provides a list of :obj:`size` instances from the :class:`Factory`,
+ through the 'create' strategy.
+
+
+ .. classmethod:: stub(cls, **kwargs)
+
+ Provides a new stub
+
+ .. classmethod:: stub_batch(cls, size, **kwargs)
+
+ Provides a list of :obj:`size` stubs from the :class:`Factory`.
+
+
+ .. classmethod:: generate(cls, strategy, **kwargs)
+
+ Provide a new instance, with the provided :obj:`strategy`.
+
+ .. classmethod:: generate_batch(cls, strategy, size, **kwargs)
+
+ Provides a list of :obj:`size` instances using the specified strategy.
+
+
+ .. classmethod:: simple_generate(cls, create, **kwargs)
+
+ Provide a new instance, either built (``create=False``) or created (``create=True``).
+
+ .. classmethod:: simple_generate_batch(cls, create, size, **kwargs)
+
+ Provides a list of :obj:`size` instances, either built or created
+ according to :obj:`create`.
+
+
+ **Extension points:**
+
+ A :class:`Factory` subclass may override a couple of class methods to adapt
+ its behaviour:
+
+ .. classmethod:: _adjust_kwargs(cls, **kwargs)
+
+ .. OHAI_VIM**
+
+ The :meth:`_adjust_kwargs` extension point allows for late fields tuning.
+
+ It is called once keyword arguments have been resolved and post-generation
+ items removed, but before the :attr:`FACTORY_ARG_PARAMETERS` extraction
+ phase.
+
+ .. code-block:: python
+
+ class UserFactory(factory.Factory):
+
+ @classmethod
+ def _adjust_kwargs(cls, **kwargs):
+ # Ensure ``lastname`` is upper-case.
+ kwargs['lastname'] = kwargs['lastname'].upper()
+ return kwargs
+
+ .. OHAI_VIM**
+
+
+ .. classmethod:: _setup_next_sequence(cls)
+
+ This method will compute the first value to use for the sequence counter
+ of this factory.
+
+ It is called when the first instance of the factory (or one of its subclasses)
+ is created.
+
+ Subclasses may fetch the next free ID from the database, for instance.
+
+
+ .. classmethod:: _build(cls, target_class, *args, **kwargs)
+
+ .. OHAI_VIM*
+
+ This class method is called whenever a new instance needs to be built.
+ It receives the target class (provided to :attr:`FACTORY_FOR`), and
+ the positional and keyword arguments to use for the class once all has
+ been computed.
+
+ Subclasses may override this for custom APIs.
+
+
+ .. classmethod:: _create(cls, target_class, *args, **kwargs)
+
+ .. OHAI_VIM*
+
+ The :meth:`_create` method is called whenever an instance needs to be
+ created.
+ It receives the same arguments as :meth:`_build`.
+
+ Subclasses may override this for specific persistence backends:
+
+ .. code-block:: python
+
+ class BaseBackendFactory(factory.Factory):
+ ABSTRACT_FACTORY = True
+
+ def _create(cls, target_class, *args, **kwargs):
+ obj = target_class(*args, **kwargs)
+ obj.save()
+ return obj
+
+ .. OHAI_VIM*
+
+
+.. _strategies:
+
+Strategies
+""""""""""
+
+factory_boy supports two main strategies for generating instances, plus stubs.
+
+
+.. data:: BUILD_STRATEGY
+
+ The 'build' strategy is used when an instance should be created,
+ but not persisted to any datastore.
+
+ It is usually a simple call to the :meth:`~object.__init__` method of the
+ :attr:`~Factory.FACTORY_FOR` class.
+
+
+.. data:: CREATE_STRATEGY
+
+ The 'create' strategy builds and saves an instance into its appropriate datastore.
+
+ This is the default strategy of factory_boy; it would typically instantiate an
+ object, then save it:
+
+ .. code-block:: pycon
+
+ >>> obj = self._associated_class(*args, **kwargs)
+ >>> obj.save()
+ >>> return obj
+
+ .. OHAI_VIM*
+
+
+.. function:: use_strategy(strategy)
+
+ *Decorator*
+
+ Change the default strategy of the decorated :class:`Factory` to the chosen :obj:`strategy`:
+
+ .. code-block:: python
+
+ @use_strategy(factory.BUILD_STRATEGY)
+ class UserBuildingFactory(UserFactory):
+ pass
+
+
+.. data:: STUB_STRATEGY
+
+ The 'stub' strategy is an exception in the factory_boy world: it doesn't return
+ an instance of the :attr:`~Factory.FACTORY_FOR` class, and actually doesn't
+ require one to be present.
+
+ Instead, it returns an instance of :class:`StubObject` whose attributes have been
+ set according to the declarations.
+
+
+.. class:: StubObject(object)
+
+ A plain, stupid object. No method, no helpers, simply a bunch of attributes.
+
+ It is typically instantiated, then has its attributes set:
+
+ .. code-block:: pycon
+
+ >>> obj = StubObject()
+ >>> obj.x = 1
+ >>> obj.y = 2
+
+
+.. class:: StubFactory(Factory)
+
+ An :attr:`abstract <Factory.ABSTRACT_FACTORY>` :class:`Factory`,
+ with a default strategy set to :data:`STUB_STRATEGY`.
+
+
+.. _declarations:
+
+Declarations
+------------
+
+LazyAttribute
+"""""""""""""
+
+.. class:: LazyAttribute(method_to_call)
+
+The :class:`LazyAttribute` is a simple yet extremely powerful building brick
+for extending a :class:`Factory`.
+
+It takes as argument a method to call (usually a lambda); that method should
+accept the object being built as sole argument, and return a value.
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ username = 'john'
+ email = factory.LazyAttribute(lambda o: '%s@example.com' % o.username)
+
+.. code-block:: pycon
+
+ >>> u = UserFactory()
+ >>> u.email
+ 'john@example.com'
+
+ >>> u = UserFactory(username='leo')
+ >>> u.email
+ 'leo@example.com'
+
+
+Decorator
+~~~~~~~~~
+
+.. function:: lazy_attribute
+
+If a simple lambda isn't enough, you may use the :meth:`lazy_attribute` decorator instead.
+
+This decorates an instance method that should take a single argument, ``self``;
+the name of the method will be used as the name of the attribute to fill with the
+return value of the method:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory)
+ FACTORY_FOR = User
+
+ name = u"Jean"
+
+ @factory.lazy_attribute
+ def email(self):
+ # Convert to plain ascii text
+ clean_name = (unicodedata.normalize('NFKD', self.name)
+ .encode('ascii', 'ignore')
+ .decode('utf8'))
+ return u'%s@example.com' % clean_name
+
+.. code-block:: pycon
+
+ >>> joel = UserFactory(name=u"Joël")
+ >>> joel.email
+ u'joel@example.com'
+
+
+Sequence
+""""""""
+
+.. class:: Sequence(lambda, type=int)
+
+If a field should be unique, and thus different for all built instances,
+use a :class:`Sequence`.
+
+This declaration takes a single argument, a function accepting a single parameter
+- the current sequence counter - and returning the related value.
+
+
+.. note:: An extra kwarg argument, ``type``, may be provided.
+ This feature is deprecated in 1.3.0 and will be removed in 2.0.0.
+
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory)
+ FACTORY_FOR = User
+
+ phone = factory.Sequence(lambda n: '123-555-%04d' % n)
+
+.. code-block:: pycon
+
+ >>> UserFactory().phone
+ '123-555-0001'
+ >>> UserFactory().phone
+ '123-555-0002'
+
+
+Decorator
+~~~~~~~~~
+
+.. function:: sequence
+
+As with :meth:`lazy_attribute`, a decorator is available for complex situations.
+
+:meth:`sequence` decorates an instance method, whose ``self`` method will actually
+be the sequence counter - this might be confusing:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory)
+ FACTORY_FOR = User
+
+ @factory.sequence
+ def phone(n):
+ a = n // 10000
+ b = n % 10000
+ return '%03d-555-%04d' % (a, b)
+
+.. code-block:: pycon
+
+ >>> UserFactory().phone
+ '000-555-9999'
+ >>> UserFactory().phone
+ '001-555-0000'
+
+
+Sharing
+~~~~~~~
+
+The sequence counter is shared across all :class:`Sequence` attributes of the
+:class:`Factory`:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ phone = factory.Sequence(lambda n: '%04d' % n)
+ office = factory.Sequence(lambda n: 'A23-B%03d' % n)
+
+.. code-block:: pycon
+
+ >>> u = UserFactory()
+ >>> u.phone, u.office
+ '0041', 'A23-B041'
+ >>> u2 = UserFactory()
+ >>> u2.phone, u2.office
+ '0042', 'A23-B042'
+
+
+Inheritance
+~~~~~~~~~~~
+
+When a :class:`Factory` inherits from another :class:`Factory`, their
+sequence counter is shared:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ phone = factory.Sequence(lambda n: '123-555-%04d' % n)
+
+
+ class EmployeeFactory(UserFactory):
+ office_phone = factory.Sequence(lambda n: '%04d' % n)
+
+.. code-block:: pycon
+
+ >>> u = UserFactory()
+ >>> u.phone
+ '123-555-0001'
+
+ >>> e = EmployeeFactory()
+ >>> e.phone, e.office_phone
+ '123-555-0002', '0002'
+
+ >>> u2 = UserFactory()
+ >>> u2.phone
+ '123-555-0003'
+
+
+LazyAttributeSequence
+"""""""""""""""""""""
+
+.. class:: LazyAttributeSequence(method_to_call)
+
+The :class:`LazyAttributeSequence` declaration merges features of :class:`Sequence`
+and :class:`LazyAttribute`.
+
+It takes a single argument, a function whose two parameters are, in order:
+
+* The object being built
+* The sequence counter
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ login = 'john'
+ email = factory.LazyAttributeSequence(lambda o, n: '%s@s%d.example.com' % (o.login, n))
+
+.. code-block:: pycon
+
+ >>> UserFactory().email
+ 'john@s1.example.com'
+ >>> UserFactory(login='jack').email
+ 'jack@s2.example.com'
+
+
+Decorator
+~~~~~~~~~
+
+.. function:: lazy_attribute_sequence(method_to_call)
+
+As for :meth:`lazy_attribute` and :meth:`sequence`, the :meth:`lazy_attribute_sequence`
+handles more complex cases:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ login = 'john'
+
+ @lazy_attribute_sequence
+ def email(self, n):
+ bucket = n % 10
+ return '%s@s%d.example.com' % (self.login, bucket)
+
+
+SubFactory
+""""""""""
+
+.. class:: SubFactory(sub_factory, **kwargs)
+
+ .. OHAI_VIM**
+
+This attribute declaration calls another :class:`Factory` subclass,
+selecting the same build strategy and collecting extra kwargs in the process.
+
+The :class:`SubFactory` attribute should be called with:
+
+* A :class:`Factory` subclass as first argument, or the fully qualified import
+ path to that :class:`Factory` (see :ref:`Circular imports <subfactory-circular>`)
+* An optional set of keyword arguments that should be passed when calling that
+ factory
+
+
+Definition
+~~~~~~~~~~
+
+.. code-block:: python
+
+
+ # A standard factory
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ # Various fields
+ first_name = 'John'
+ last_name = factory.Sequence(lambda n: 'D%se' % ('o' * n)) # De, Doe, Dooe, Doooe, ...
+ email = factory.LazyAttribute(lambda o: '%s.%s@example.org' % (o.first_name.lower(), o.last_name.lower()))
+
+ # A factory for an object with a 'User' field
+ class CompanyFactory(factory.Factory):
+ FACTORY_FOR = Company
+
+ name = factory.Sequence(lambda n: 'FactoryBoyz' + 'z' * n)
+
+ # Let's use our UserFactory to create that user, and override its first name.
+ owner = factory.SubFactory(UserFactory, first_name='Jack')
+
+
+Calling
+~~~~~~~
+
+The wrapping factory will call of the inner factory:
+
+.. code-block:: pycon
+
+ >>> c = CompanyFactory()
+ >>> c
+ <Company: FactoryBoyz>
+
+ # Notice that the first_name was overridden
+ >>> c.owner
+ <User: Jack De>
+ >>> c.owner.email
+ jack.de@example.org
+
+
+Fields of the :class:`~factory.SubFactory` may be overridden from the external factory:
+
+.. code-block:: pycon
+
+ >>> c = CompanyFactory(owner__first_name='Henry')
+ >>> c.owner
+ <User: Henry Doe>
+
+ # Notice that the updated first_name was propagated to the email LazyAttribute.
+ >>> c.owner.email
+ henry.doe@example.org
+
+ # It is also possible to override other fields of the SubFactory
+ >>> c = CompanyFactory(owner__last_name='Jones')
+ >>> c.owner
+ <User: Henry Jones>
+ >>> c.owner.email
+ henry.jones@example.org
+
+
+Strategies
+~~~~~~~~~~
+
+The strategy chosen for the external factory will be propagated to all subfactories:
+
+.. code-block:: pycon
+
+ >>> c = CompanyFactory()
+ >>> c.pk # Saved to the database
+ 3
+ >>> c.owner.pk # Saved to the database
+ 8
+
+ >>> c = CompanyFactory.build()
+ >>> c.pk # Not saved
+ None
+ >>> c.owner.pk # Not saved either
+ None
+
+
+.. _subfactory-circular:
+
+Circular imports
+~~~~~~~~~~~~~~~~
+
+Some factories may rely on each other in a circular manner.
+This issue can be handled by passing the absolute import path to the target
+:class:`Factory` to the :class:`SubFactory`:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ username = 'john'
+ main_group = factory.SubFactory('users.factories.GroupFactory')
+
+ class GroupFactory(factory.Factory):
+ FACTORY_FOR = Group
+
+ name = "MyGroup"
+ owner = factory.SubFactory(UserFactory)
+
+
+Obviously, such circular relationships require careful handling of loops:
+
+.. code-block:: pycon
+
+ >>> owner = UserFactory(main_group=None)
+ >>> UserFactory(main_group__owner=owner)
+ <john (group: MyGroup)>
+
+
+SelfAttribute
+"""""""""""""
+
+.. class:: SelfAttribute(dotted_path_to_attribute)
+
+Some fields should reference another field of the object being constructed, or an attribute thereof.
+
+This is performed by the :class:`~factory.SelfAttribute` declaration.
+That declaration takes a single argument, a dot-delimited path to the attribute to fetch:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory)
+ FACTORY_FOR = User
+
+ birthdate = factory.Sequence(lambda n: datetime.date(2000, 1, 1) + datetime.timedelta(days=n))
+ birthmonth = factory.SelfAttribute('birthdate.month')
+
+.. code-block:: pycon
+
+ >>> u = UserFactory()
+ >>> u.birthdate
+ date(2000, 3, 15)
+ >>> u.birthmonth
+ 3
+
+
+Parents
+~~~~~~~
+
+When used in conjunction with :class:`~factory.SubFactory`, the :class:`~factory.SelfAttribute`
+gains an "upward" semantic through the double-dot notation, as used in Python imports.
+
+``factory.SelfAttribute('..country.language')`` means
+"Select the ``language`` of the ``country`` of the :class:`~factory.Factory` calling me".
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ language = 'en'
+
+
+ class CompanyFactory(factory.Factory):
+ FACTORY_FOR = Company
+
+ country = factory.SubFactory(CountryFactory)
+ owner = factory.SubFactory(UserFactory, language=factory.SelfAttribute('..country.language'))
+
+.. code-block:: pycon
+
+ >>> company = CompanyFactory()
+ >>> company.country.language
+ 'fr'
+ >>> company.owner.language
+ 'fr'
+
+Obviously, this "follow parents" hability also handles overriding some attributes on call:
+
+.. code-block:: pycon
+
+ >>> company = CompanyFactory(country=china)
+ >>> company.owner.language
+ 'cn'
+
+
+Iterator
+""""""""
+
+.. class:: Iterator(iterable, cycle=True, getter=None)
+
+The :class:`Iterator` declaration takes succesive values from the given
+iterable. When it is exhausted, it starts again from zero (unless ``cycle=False``).
+
+.. note:: Versions prior to 1.3.0 declared both :class:`Iterator` (for ``cycle=False``)
+ and :class:`InfiniteIterator` (for ``cycle=True``).
+
+ :class:`InfiniteIterator` is deprecated as of 1.3.0 and will be removed in 2.0.0
+
+The ``cycle`` argument is only useful for advanced cases, where the provided
+iterable has no end (as wishing to cycle it means storing values in memory...).
+
+Each call to the factory will receive the next value from the iterable:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory)
+ lang = factory.Iterator(['en', 'fr', 'es', 'it', 'de'])
+
+.. code-block:: pycon
+
+ >>> UserFactory().lang
+ 'en'
+ >>> UserFactory().lang
+ 'fr'
+
+
+When a value is passed in for the argument, the iterator will *not* be advanced:
+
+.. code-block:: pycon
+
+ >>> UserFactory().lang
+ 'en'
+ >>> UserFactory(lang='cn').lang
+ 'cn'
+ >>> UserFactory().lang
+ 'fr'
+
+Getter
+~~~~~~
+
+Some situations may reuse an existing iterable, using only some component.
+This is handled by the :attr:`~Iterator.getter` attribute: this is a function
+that accepts as sole parameter a value from the iterable, and returns an
+adequate value.
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ # CATEGORY_CHOICES is a list of (key, title) tuples
+ category = factory.Iterator(User.CATEGORY_CHOICES, getter=lambda c: c[0])
+
+
+post-building hooks
+"""""""""""""""""""
+
+Some objects expect additional method calls or complex processing for proper definition.
+For instance, a ``User`` may need to have a related ``Profile``, where the ``Profile`` is built from the ``User`` object.
+
+To support this pattern, factory_boy provides the following tools:
+ - :class:`PostGeneration`: this class allows calling a given function with the generated object as argument
+ - :func:`post_generation`: decorator performing the same functions as :class:`PostGeneration`
+ - :class:`RelatedFactory`: this builds or creates a given factory *after* building/creating the first Factory.
+
+
+RelatedFactory
+""""""""""""""
+
+.. class:: RelatedFactory(some_factory, related_field, **kwargs)
+
+ .. OHAI_VIM**
+
+A :class:`RelatedFactory` behaves mostly like a :class:`SubFactory`,
+with the main difference that it should be provided with a ``related_field`` name
+as second argument.
+
+Once the base object has been built (or created), the :class:`RelatedFactory` will
+build the :class:`Factory` passed as first argument (with the same strategy),
+passing in the base object as a keyword argument whose name is passed in the
+``related_field`` argument:
+
+.. code-block:: python
+
+ class CityFactory(factory.Factory):
+ FACTORY_FOR = City
+
+ capital_of = None
+ name = "Toronto"
+
+ class CountryFactory(factory.Factory):
+ FACTORY_FOR = Country
+
+ lang = 'fr'
+ capital_city = factory.RelatedFactory(CityFactory, 'capital_of', name="Paris")
+
+.. code-block:: pycon
+
+ >>> france = CountryFactory()
+ >>> City.objects.get(capital_of=france)
+ <City: Paris>
+
+Extra kwargs may be passed to the related factory, through the usual ``ATTR__SUBATTR`` syntax:
+
+.. code-block:: pycon
+
+ >>> england = CountryFactory(lang='en', capital_city__name="London")
+ >>> City.objects.get(capital_of=england)
+ <City: London>
+
+
+PostGeneration
+""""""""""""""
+
+.. class:: PostGeneration(callable)
+
+The :class:`PostGeneration` declaration performs actions once the target object
+has been generated.
+
+Its sole argument is a callable, that will be called once the base object has
+ been generated.
+
+.. note:: Previous versions of factory_boy supported an extra ``extract_prefix``
+ argument, to use an alternate argument prefix.
+ This feature is deprecated in 1.3.0 and will be removed in 2.0.0.
+
+Once the base object has been generated, the provided callable will be called
+as ``callable(obj, create, extracted, **kwargs)``, where:
+
+- ``obj`` is the base object previously generated
+- ``create`` is a boolean indicating which strategy was used
+- ``extracted`` is ``None`` unless a value was passed in for the
+ :class:`PostGeneration` declaration at :class:`Factory` declaration time
+- ``kwargs`` are any extra parameters passed as ``attr__key=value`` when calling
+ the :class:`Factory`:
+
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ login = 'john'
+ make_mbox = factory.PostGeneration(
+ lambda obj, create, extracted, **kwargs: os.makedirs(obj.login))
+
+.. OHAI_VIM**
+
+Decorator
+~~~~~~~~~
+
+.. function:: post_generation(extract_prefix=None)
+
+A decorator is also provided, decorating a single method accepting the same
+``obj``, ``created``, ``extracted`` and keyword arguments as :class:`PostGeneration`.
+
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ login = 'john'
+
+ @factory.post_generation
+ def mbox(self, create, extracted, **kwargs):
+ if not create:
+ return
+ path = extracted or os.path.join('/tmp/mbox/', self.login)
+ os.path.makedirs(path)
+ return path
+
+.. OHAI_VIM**
+
+.. code-block:: pycon
+
+ >>> UserFactory.build() # Nothing was created
+ >>> UserFactory.create() # Creates dir /tmp/mbox/john
+ >>> UserFactory.create(login='jack') # Creates dir /tmp/mbox/jack
+ >>> UserFactory.create(mbox='/tmp/alt') # Creates dir /tmp/alt
+
+
+PostGenerationMethodCall
+""""""""""""""""""""""""
+
+.. class:: PostGenerationMethodCall(method_name, extract_prefix=None, *args, **kwargs)
+
+.. OHAI_VIM*
+
+The :class:`PostGenerationMethodCall` declaration will call a method on the
+generated object just after it being called.
+
+Its sole argument is the name of the method to call.
+Extra arguments and keyword arguments for the target method may also be provided.
+
+Once the object has been generated, the method will be called, with the arguments
+provided in the :class:`PostGenerationMethodCall` declaration, and keyword
+arguments taken from the combination of :class:`PostGenerationMethodCall`
+declaration and prefix-based values:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ password = factory.PostGenerationMethodCall('set_password', password='')
+
+.. code-block:: pycon
+
+ >>> UserFactory() # Calls user.set_password(password='')
+ >>> UserFactory(password='test') # Calls user.set_password(password='test')
+ >>> UserFactory(password__disabled=True) # Calls user.set_password(password='', disabled=True)
+
+
+Module-level functions
+----------------------
+
+Beyond the :class:`Factory` class and the various :ref:`declarations` classes
+and methods, factory_boy exposes a few module-level functions, mostly useful
+for lightweight factory generation.
+
+
+Lightweight factory declaration
+"""""""""""""""""""""""""""""""
+
+.. function:: make_factory(klass, **kwargs)
+
+ .. OHAI_VIM**
+
+ The :func:`make_factory` function takes a class, declarations as keyword arguments,
+ and generates a new :class:`Factory` for that class accordingly:
+
+ .. code-block:: python
+
+ UserFactory = make_factory(User,
+ login='john',
+ email=factory.LazyAttribute(lambda u: '%s@example.com' % u.login),
+ )
+
+ # This is equivalent to:
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ login = 'john'
+ email = factory.LazyAttribute(lambda u: '%s@example.com' % u.login)
+
+
+Instance building
+"""""""""""""""""
+
+The :mod:`factory` module provides a bunch of shortcuts for creating a factory and
+extracting instances from them:
+
+.. function:: build(klass, **kwargs)
+.. function:: build_batch(klass, size, **kwargs)
+
+ Create a factory for :obj:`klass` using declarations passed in kwargs;
+ return an instance built from that factory,
+ or a list of :obj:`size` instances (for :func:`build_batch`).
+
+ :param class klass: Class of the instance to build
+ :param int size: Number of instances to build
+ :param kwargs: Declarations to use for the generated factory
+
+
+
+.. function:: create(klass, **kwargs)
+.. function:: create_batch(klass, size, **kwargs)
+
+ Create a factory for :obj:`klass` using declarations passed in kwargs;
+ return an instance created from that factory,
+ or a list of :obj:`size` instances (for :func:`create_batch`).
+
+ :param class klass: Class of the instance to create
+ :param int size: Number of instances to create
+ :param kwargs: Declarations to use for the generated factory
+
+
+
+.. function:: stub(klass, **kwargs)
+.. function:: stub_batch(klass, size, **kwargs)
+
+ Create a factory for :obj:`klass` using declarations passed in kwargs;
+ return an instance stubbed from that factory,
+ or a list of :obj:`size` instances (for :func:`stub_batch`).
+
+ :param class klass: Class of the instance to stub
+ :param int size: Number of instances to stub
+ :param kwargs: Declarations to use for the generated factory
+
+
+
+.. function:: generate(klass, strategy, **kwargs)
+.. function:: generate_batch(klass, strategy, size, **kwargs)
+
+ Create a factory for :obj:`klass` using declarations passed in kwargs;
+ return an instance generated from that factory with the :obj:`strategy` strategy,
+ or a list of :obj:`size` instances (for :func:`generate_batch`).
+
+ :param class klass: Class of the instance to generate
+ :param str strategy: The strategy to use
+ :param int size: Number of instances to generate
+ :param kwargs: Declarations to use for the generated factory
+
+
+
+.. function:: simple_generate(klass, create, **kwargs)
+.. function:: simple_generate_batch(klass, create, size, **kwargs)
+
+ Create a factory for :obj:`klass` using declarations passed in kwargs;
+ return an instance generated from that factory according to the :obj:`create` flag,
+ or a list of :obj:`size` instances (for :func:`simple_generate_batch`).
+
+ :param class klass: Class of the instance to generate
+ :param bool create: Whether to build (``False``) or create (``True``) instances
+ :param int size: Number of instances to generate
+ :param kwargs: Declarations to use for the generated factory
+
+
diff --git a/docs/subfactory.rst b/docs/subfactory.rst
deleted file mode 100644
index f8642f3..0000000
--- a/docs/subfactory.rst
+++ /dev/null
@@ -1,81 +0,0 @@
-SubFactory
-==========
-
-Some objects may use other complex objects as parameters; in order to simplify this setup, factory_boy
-provides the :py:class:`factory.SubFactory` class.
-
-This should be used when defining a :py:class:`~factory.Factory` attribute that will hold the other complex object::
-
- import factory
-
- # A standard factory
- class UserFactory(factory.Factory):
- FACTORY_FOR = User
-
- # Various fields
- first_name = 'John'
- last_name = factory.Sequence(lambda n: 'D%se' % ('o' * n)) # De, Doe, Dooe, Doooe, ...
- email = factory.LazyAttribute(lambda o: '%s.%s@example.org' % (o.first_name.lower(), o.last_name.lower()))
-
- # A factory for an object with a 'User' field
- class CompanyFactory(factory.Factory):
- FACTORY_FOR = Company
-
- name = factory.Sequence(lambda n: 'FactoryBoyz' + z * n)
-
- # Let's use our UserFactory to create that user, and override its first name.
- owner = factory.SubFactory(UserFactory, first_name='Jack')
-
-Instantiating the external factory will in turn instantiate an object of the internal factory::
-
- >>> c = CompanyFactory()
- >>> c
- <Company: FactoryBoyz>
-
- # Notice that the first_name was overridden
- >>> c.owner
- <User: Jack De>
- >>> c.owner.email
- jack.de@example.org
-
-Fields of the SubFactory can also be overridden when instantiating the external factory::
-
- >>> c = CompanyFactory(owner__first_name='Henry')
- >>> c.owner
- <User: Henry Doe>
-
- # Notice that the updated first_name was propagated to the email LazyAttribute.
- >>> c.owner.email
- henry.doe@example.org
-
- # It is also possible to override other fields of the SubFactory
- >>> c = CompanyFactory(owner__last_name='Jones')
- >>> c.owner
- <User: Henry Jones>
- >>> c.owner.email
- henry.jones@example.org
-
-
-Circular dependencies
----------------------
-
-In order to solve circular dependency issues, Factory Boy provides the :class:`~factory.CircularSubFactory` class.
-
-This class expects a module name and a factory name to import from that module; the given module will be imported
-(as an absolute import) when the factory is first accessed::
-
- # foo/factories.py
- import factory
-
- from bar import factories
-
- class FooFactory(factory.Factory):
- bar = factory.SubFactory(factories.BarFactory)
-
-
- # bar/factories.py
- import factory
-
- class BarFactory(factory.Factory):
- # Avoid circular import
- foo = factory.CircularSubFactory('foo.factories', 'FooFactory', bar=None)