summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorThomas Goirand <thomas@goirand.fr>2013-05-12 05:32:34 +0000
committerThomas Goirand <thomas@goirand.fr>2013-05-12 05:32:34 +0000
commit28991f9514e3cd78a528bbbe956d9b4536c416e0 (patch)
treea3871392d2382f60490824d79058f8a71ae1c34e /docs
parent57fa2e21aed37c1af2a87f36a998046b73092a21 (diff)
parent876845102c4a217496d0f6435bfe1e3726d31fe4 (diff)
downloadfactory-boy-28991f9514e3cd78a528bbbe956d9b4536c416e0.tar
factory-boy-28991f9514e3cd78a528bbbe956d9b4536c416e0.tar.gz
Merge tag '2.0.2' into debian/unstable
Release of factory_boy 2.0.2 Conflicts: docs/changelog.rst docs/index.rst docs/subfactory.rst tests/cyclic/bar.py tests/cyclic/foo.py
Diffstat (limited to 'docs')
-rw-r--r--docs/conf.py10
-rw-r--r--docs/examples.rst28
-rw-r--r--docs/fuzzy.rst88
-rw-r--r--docs/ideas.rst8
-rw-r--r--docs/internals.rst27
-rw-r--r--docs/introduction.rst258
-rw-r--r--docs/orms.rst70
-rw-r--r--docs/post_generation.rst91
-rw-r--r--docs/recipes.rst234
-rw-r--r--docs/reference.rst1409
-rw-r--r--docs/subfactory.rst56
11 files changed, 2098 insertions, 181 deletions
diff --git a/docs/conf.py b/docs/conf.py
index db523c3..47630d3 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
@@ -218,4 +218,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/fuzzy.rst b/docs/fuzzy.rst
new file mode 100644
index 0000000..f1f4085
--- /dev/null
+++ b/docs/fuzzy.rst
@@ -0,0 +1,88 @@
+Fuzzy attributes
+================
+
+.. module:: factory.fuzzy
+
+Some tests may be interested in testing with fuzzy, random values.
+
+This is handled by the :mod:`factory.fuzzy` module, which provides a few
+random declarations.
+
+
+FuzzyAttribute
+--------------
+
+
+.. class:: FuzzyAttribute
+
+ The :class:`FuzzyAttribute` uses an arbitrary callable as fuzzer.
+ It is expected that successive calls of that function return various
+ values.
+
+ .. attribute:: fuzzer
+
+ The callable that generates random values
+
+
+FuzzyChoice
+-----------
+
+
+.. class:: FuzzyChoice(choices)
+
+ The :class:`FuzzyChoice` fuzzer yields random choices from the given
+ iterable.
+
+ .. note:: The passed in :attr:`choices` will be converted into a list at
+ declaration time.
+
+ .. attribute:: choices
+
+ The list of choices to select randomly
+
+
+FuzzyInteger
+------------
+
+.. class:: FuzzyInteger(low[, high])
+
+ The :class:`FuzzyInteger` fuzzer generates random integers within a given
+ inclusive range.
+
+ The :attr:`low` bound may be omitted, in which case it defaults to 0:
+
+ .. code-block:: pycon
+
+ >>> FuzzyInteger(0, 42)
+ >>> fi.low, fi.high
+ 0, 42
+
+ >>> fi = FuzzyInteger(42)
+ >>> fi.low, fi.high
+ 0, 42
+
+ .. attribute:: low
+
+ int, the inclusive lower bound of generated integers
+
+ .. attribute:: high
+
+ int, the inclusive higher bound of generated integers
+
+
+Custom fuzzy fields
+-------------------
+
+Alternate fuzzy fields may be defined.
+They should inherit from the :class:`BaseFuzzyAttribute` class, and override its
+:meth:`~BaseFuzzyAttribute.fuzz` method.
+
+
+.. class:: BaseFuzzyAttribute
+
+ Base class for all fuzzy attributes.
+
+ .. method:: fuzz(self)
+
+ The method responsible for generating random values.
+ *Must* be overridden in subclasses.
diff --git a/docs/ideas.rst b/docs/ideas.rst
new file mode 100644
index 0000000..914e640
--- /dev/null
+++ b/docs/ideas.rst
@@ -0,0 +1,8 @@
+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
+
diff --git a/docs/internals.rst b/docs/internals.rst
index 279c4e9..a7402ff 100644
--- a/docs/internals.rst
+++ b/docs/internals.rst
@@ -1,25 +1,2 @@
-Factory Boy's internals
-======================
-
-
-declarations
-------------
-
-.. automodule:: factory.declarations
- :members:
-
-
-containers
-----------
-
-.. automodule:: factory.containers
- :members:
-
-
-
-base
-----
-
-.. automodule:: factory.base
- :members:
-
+Internals
+=========
diff --git a/docs/introduction.rst b/docs/introduction.rst
new file mode 100644
index 0000000..8bbb10c
--- /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.FACTROY_STRATEGY` attribute.
+
+
diff --git a/docs/orms.rst b/docs/orms.rst
new file mode 100644
index 0000000..8e5b6f6
--- /dev/null
+++ b/docs/orms.rst
@@ -0,0 +1,70 @@
+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` or :class:`~factory.PostGeneration`
+ attributes, the base object will be :meth:`saved <django.db.models.Model.save>`
+ once all post-generation hooks have run.
+
+ .. attribute:: FACTORY_DJANGO_GET_OR_CREATE
+
+ Fields whose name are passed in this list will be used to perform a
+ :meth:`Model.objects.get_or_create() <django.db.models.query.QuerySet.get_or_create>`
+ instead of the usual :meth:`Model.objects.create() <django.db.models.query.QuerySet.create>`:
+
+ .. code-block:: python
+
+ class UserFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.User
+ FACTORY_DJANGO_GET_OR_CREATE = ('username',)
+
+ username = 'john'
+
+ .. code-block:: pycon
+
+ >>> User.objects.all()
+ []
+ >>> UserFactory() # Creates a new user
+ <User: john>
+ >>> User.objects.all()
+ [<User: john>]
+
+ >>> UserFactory() # Fetches the existing user
+ <User: john>
+ >>> User.objects.all() # No new user!
+ [<User: john>]
+
+ >>> UserFactory(username='jack') # Creates another user
+ <User: jack>
+ >>> User.objects.all()
+ [<User: john>, <User: jack>]
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..e226732
--- /dev/null
+++ b/docs/recipes.rst
@@ -0,0 +1,234 @@
+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.DjangoModelFactory):
+ 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.DjangoModelFactory):
+ 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``.
+
+
+Simple ManyToMany
+-----------------
+
+Building the adequate link between two models depends heavily on the use case;
+factory_boy doesn't provide a "all in one tools" as for :class:`~factory.SubFactory`
+or :class:`~factory.RelatedFactory`, users will have to craft their own depending
+on the model.
+
+The base building block for this feature is the :class:`~factory.post_generation`
+hook:
+
+.. code-block:: python
+
+ # models.py
+ class Group(models.Model):
+ name = models.CharField()
+
+ class User(models.Model):
+ name = models.CharField()
+ groups = models.ManyToMany(Group)
+
+
+ # factories.py
+ class GroupFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.Group
+
+ name = factory.Sequence(lambda n: "Group #%s" % n)
+
+ class UserFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.User
+
+ name = "John Doe"
+
+ @factory.post_generation
+ def groups(self, create, extracted, **kwargs):
+ if not create:
+ # Simple build, do nothing.
+ return
+
+ if extracted:
+ # A list of groups were passed in, use them
+ for group in extracted:
+ self.groups.add(group)
+
+.. OHAI_VIM**
+
+When calling ``UserFactory()`` or ``UserFactory.build()``, no group binding
+will be created.
+
+But when ``UserFactory.create(groups=(group1, group2, group3))`` is called,
+the ``groups`` declaration will add passed in groups to the set of groups for the
+user.
+
+
+ManyToMany with a 'through'
+---------------------------
+
+
+If only one link is required, this can be simply performed with a :class:`RelatedFactory`.
+If more links are needed, simply add more :class:`RelatedFactory` declarations:
+
+.. code-block:: python
+
+ # models.py
+ class User(models.Model):
+ name = models.CharField()
+
+ class Group(models.Model):
+ name = models.CharField()
+ members = models.ManyToMany(User, through='GroupLevel')
+
+ class GroupLevel(models.Model):
+ user = models.ForeignKey(User)
+ group = models.ForeignKey(Group)
+ rank = models.IntegerField()
+
+
+ # factories.py
+ class UserFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.User
+
+ name = "John Doe"
+
+ class GroupFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.Group
+
+ name = "Admins"
+
+ class GroupLevelFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.GroupLevel
+
+ user = factory.SubFactory(UserFactory)
+ group = factory.SubFactory(GroupFactory)
+ rank = 1
+
+ class UserWithGroupFactory(UserFactory):
+ membership = factory.RelatedFactory(GroupLevelFactory, 'user')
+
+ class UserWith2GroupsFactory(UserFactory):
+ membership1 = factory.RelatedFactory(GroupLevelFactory, 'user', group__name='Group1')
+ membership2 = factory.RelatedFactory(GroupLevelFactory, 'user', group__name='Group2')
+
+
+Whenever the ``UserWithGroupFactory`` is called, it will, as a post-generation hook,
+call the ``GroupLevelFactory``, passing the generated user as a ``user`` field:
+
+1. ``UserWithGroupFactory()`` generates a ``User`` instance, ``obj``
+2. It calls ``GroupLevelFactory(user=obj)``
+3. It returns ``obj``
+
+
+When using the ``UserWith2GroupsFactory``, that behavior becomes:
+
+1. ``UserWith2GroupsFactory()`` generates a ``User`` instance, ``obj``
+2. It calls ``GroupLevelFactory(user=obj, group__name='Group1')``
+3. It calls ``GroupLevelFactory(user=obj, group__name='Group2')``
+4. It returns ``obj``
+
+
+Copying fields to a SubFactory
+------------------------------
+
+When a field of a related class should match one of the container:
+
+
+.. code-block:: python
+
+ # models.py
+ class Country(models.Model):
+ name = models.CharField()
+ lang = models.CharField()
+
+ class User(models.Model):
+ name = models.CharField()
+ lang = models.CharField()
+ country = models.ForeignKey(Country)
+
+ class Company(models.Model):
+ name = models.CharField()
+ owner = models.ForeignKey(User)
+ country = models.ForeignKey(Country)
+
+
+Here, we want:
+
+- The User to have the lang of its country (``factory.SelfAttribute('country.lang')``)
+- The Company owner to live in the country of the company (``factory.SelfAttribute('..country')``)
+
+.. code-block:: python
+
+ # factories.py
+ class CountryFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.Country
+
+ name = factory.Iterator(["France", "Italy", "Spain"])
+ lang = factory.Iterator(['fr', 'it', 'es'])
+
+ class UserFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.User
+
+ name = "John"
+ lang = factory.SelfAttribute('country.lang')
+ country = factory.SubFactory(CountryFactory)
+
+ class CompanyFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.Company
+
+ name = "ACME, Inc."
+ country = factory.SubFactory(CountryFactory)
+ owner = factory.SubFactory(UserFactory, country=factory.SelfAttribute('..country'))
diff --git a/docs/reference.rst b/docs/reference.rst
new file mode 100644
index 0000000..81aa645
--- /dev/null
+++ b/docs/reference.rst
@@ -0,0 +1,1409 @@
+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
+
+ .. attribute:: FACTORY_HIDDEN_ARGS
+
+ While writing a :class:`Factory` for some object, it may be useful to
+ have general fields helping defining others, but that should not be
+ passed to the target class; for instance, a field named 'now' that would
+ hold a reference time used by other objects.
+
+ Factory fields whose name are listed in :attr:`FACTORY_HIDDEN_ARGS` will
+ be removed from the set of args/kwargs passed to the underlying class;
+ they can be any valid factory_boy declaration:
+
+ .. code-block:: python
+
+ class OrderFactory(factory.Factory):
+ FACTORY_FOR = Order
+ FACTORY_HIDDEN_ARGS = ('now',)
+
+ now = factory.LazyAttribute(lambda o: datetime.datetime.utcnow())
+ started_at = factory.LazyAttribute(lambda o: o.now - datetime.timedelta(hours=1))
+ paid_at = factory.LazyAttribute(lambda o: o.now - datetime.timedelta(minutes=50))
+
+ .. code-block:: pycon
+
+ >>> OrderFactory() # The value of 'now' isn't passed to Order()
+ <Order: started 2013-04-01 12:00:00, paid 2013-04-01 12:10:00>
+
+ >>> # An alternate value may be passed for 'now'
+ >>> OrderFactory(now=datetime.datetime(2013, 4, 1, 10))
+ <Order: started 2013-04-01 09:00:00, paid 2013-04-01 09:10:00>
+
+
+ **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*
+
+ .. classmethod:: _after_postgeneration(cls, obj, create, results=None)
+
+ :arg object obj: The object just generated
+ :arg bool create: Whether the object was 'built' or 'created'
+ :arg dict results: Map of post-generation declaration name to call
+ result
+
+ The :meth:`_after_postgeneration` is called once post-generation
+ declarations have been handled.
+
+ Its arguments allow to handle specifically some post-generation return
+ values, for instance.
+
+
+.. _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*
+
+ .. warning:: For backward compatibility reasons, the default behaviour of
+ factory_boy is to call ``MyClass.objects.create(*args, **kwargs)``
+ when using the ``create`` strategy.
+
+ That policy will be used if the
+ :attr:`associated class <Factory.FACTORY_FOR>` has an ``objects``
+ attribute *and* the :meth:`~Factory._create` classmethod of the
+ :class:`Factory` wasn't overridden.
+
+
+.. 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'
+
+
+Forcing a sequence counter
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If a specific value of the sequence counter is required for one instance, the
+``__sequence`` keyword argument should be passed to the factory method.
+
+This will force the sequence counter during the call, without altering the
+class-level value.
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ uid = factory.Sequence(int)
+
+.. code-block:: pycon
+
+ >>> UserFactory()
+ <User: 0>
+ >>> UserFactory()
+ <User: 1>
+ >>> UserFactory(__sequence=42)
+ <User: 42>
+
+
+.. warning:: The impact of setting ``__sequence=n`` on a ``_batch`` call is
+ undefined. Each generated instance may share a same counter, or
+ use incremental values starting from the forced value.
+
+
+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`.
+
+.. versionadded:: 1.3.0
+
+.. 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``).
+
+ .. attribute:: cycle
+
+ 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...).
+
+ .. versionadded:: 1.3.0
+ The ``cycle`` argument is available as of v1.3.0; previous versions
+ had a behaviour equivalent to ``cycle=False``.
+
+ .. attribute:: getter
+
+ A custom function called on each value returned by the iterable.
+ See the :ref:`iterator-getter` section for details.
+
+ .. versionadded:: 1.3.0
+
+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'
+
+.. _iterator-getter:
+
+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])
+
+
+Decorator
+~~~~~~~~~
+
+.. function:: iterator(func)
+
+
+When generating items of the iterator gets too complex for a simple list comprehension,
+use the :func:`iterator` decorator:
+
+.. warning:: The decorated function takes **no** argument,
+ notably no ``self`` parameter.
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ @factory.iterator
+ def name():
+ with open('test/data/names.dat', 'r') as f:
+ for line in f:
+ yield line
+
+
+Dict and List
+"""""""""""""
+
+When a factory expects lists or dicts as arguments, such values can be generated
+through the whole range of factory_boy declarations,
+with the :class:`Dict` and :class:`List` attributes:
+
+.. class:: Dict(params[, dict_factory=factory.DictFactory])
+
+ The :class:`Dict` class is used for dict-like attributes.
+ It receives as non-keyword argument a dictionary of fields to define, whose
+ value may be any factory-enabled declarations:
+
+ .. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ is_superuser = False
+ roles = factory.Dict({
+ 'role1': True,
+ 'role2': False,
+ 'role3': factory.Iterator([True, False]),
+ 'admin': factory.SelfAttribute('..is_superuser'),
+ })
+
+ .. note:: Declarations used as a :class:`Dict` values are evaluated within
+ that :class:`Dict`'s context; this means that you must use
+ the ``..foo`` syntax to access fields defined at the factory level.
+
+ On the other hand, the :class:`Sequence` counter is aligned on the
+ containing factory's one.
+
+
+ The :class:`Dict` behaviour can be tuned through the following parameters:
+
+ .. attribute:: dict_factory
+
+ The actual factory to use for generating the dict can be set as a keyword
+ argument, if an exotic dictionary-like object (SortedDict, ...) is required.
+
+
+.. class:: List(items[, list_factory=factory.ListFactory])
+
+ The :class:`List` can be used for list-like attributes.
+
+ Internally, the fields are converted into a ``index=value`` dict, which
+ makes it possible to override some values at use time:
+
+ .. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ flags = factory.List([
+ 'user',
+ 'active',
+ 'admin',
+ ])
+
+ .. code-block:: pycon
+
+ >>> u = UserFactory(flags__2='superadmin')
+ >>> u.flags
+ ['user', 'active', 'superadmin']
+
+
+ The :class:`List` behaviour can be tuned through the following parameters:
+
+ .. attribute:: list_factory
+
+ The actual factory to use for generating the list can be set as a keyword
+ argument, if another type (tuple, set, ...) is required.
+
+
+Post-generation 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:`PostGenerationMethodCall`: allows you to hook a particular attribute to a function call
+ - :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.
+
+
+Extracting parameters
+"""""""""""""""""""""
+
+All post-building hooks share a common base for picking parameters from the
+set of attributes passed to the :class:`Factory`.
+
+For instance, a :class:`PostGeneration` hook is declared as ``post``:
+
+.. code-block:: python
+
+ class SomeFactory(factory.Factory):
+ FACTORY_FOR = SomeObject
+
+ @post_generation
+ def post(self, create, extracted, **kwargs):
+ obj.set_origin(create)
+
+.. OHAI_VIM**
+
+
+When calling the factory, some arguments will be extracted for this method:
+
+- If a ``post`` argument is passed, it will be passed as the ``extracted`` field
+- Any argument starting with ``post__XYZ`` will be extracted, its ``post__`` prefix
+ removed, and added to the kwargs passed to the post-generation hook.
+
+Extracted arguments won't be passed to the :attr:`~Factory.FACTORY_FOR` class.
+
+Thus, in the following call:
+
+.. code-block:: pycon
+
+ >>> SomeFactory(
+ post=1,
+ post_x=2,
+ post__y=3,
+ post__z__t=42,
+ )
+
+The ``post`` hook will receive ``1`` as ``extracted`` and ``{'y': 3, 'z__t': 42}``
+as keyword arguments; ``{'post_x': 2}`` will be passed to ``SomeFactory.FACTORY_FOR``.
+
+
+RelatedFactory
+""""""""""""""
+
+.. class:: RelatedFactory(factory, name='', **kwargs)
+
+ .. OHAI_VIM**
+
+ A :class:`RelatedFactory` behaves mostly like a :class:`SubFactory`,
+ with the main difference that the related :class:`Factory` will be generated
+ *after* the base :class:`Factory`.
+
+
+ .. attribute:: factory
+
+ As for :class:`SubFactory`, the :attr:`factory` argument can be:
+
+ - A :class:`Factory` subclass
+ - Or the fully qualified path to a :class:`Factory` subclass
+ (see :ref:`subfactory-circular` for details)
+
+ .. attribute:: name
+
+ The generated object (where the :class:`RelatedFactory` attribute will
+ set) may be passed to the related factory if the :attr:`name` parameter
+ is set.
+
+ It will be passed as a keyword argument, using the :attr:`name` value as
+ keyword:
+
+
+.. 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.
+
+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
+
+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, *args, **kwargs)
+
+ .. OHAI_VIM*
+
+ The :class:`PostGenerationMethodCall` declaration will call a method on
+ the generated object just after instantiation. This declaration class
+ provides a friendly means of generating attributes of a factory instance
+ during initialization. The declaration is created using the following arguments:
+
+ .. attribute:: method_name
+
+ The name of the method to call on the :attr:`~Factory.FACTORY_FOR` object
+
+ .. attribute:: args
+
+ The default set of unnamed arguments to pass to the method given in
+ :attr:`method_name`
+
+ .. attribute:: kwargs
+
+ The default set of keyword arguments to pass to the method given in
+ :attr:`method_name`
+
+Once the factory instance has been generated, the method specified in
+:attr:`~PostGenerationMethodCall.method_name` will be called on the generated object
+with any arguments specified in the :class:`PostGenerationMethodCall` declaration, by
+default.
+
+For example, to set a default password on a generated User instance
+during instantiation, we could make a declaration for a ``password``
+attribute like below:
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ username = 'user'
+ password = factory.PostGenerationMethodCall('set_password',
+ 'defaultpassword')
+
+When we instantiate a user from the ``UserFactory``, the factory
+will create a password attribute by calling ``User.set_password('defaultpassword')``.
+Thus, by default, our users will have a password set to ``'defaultpassword'``.
+
+.. code-block:: pycon
+
+ >>> u = UserFactory() # Calls user.set_password('defaultpassword')
+ >>> u.check_password('defaultpassword')
+ True
+
+If the :class:`PostGenerationMethodCall` declaration contained no
+arguments or one argument, an overriding the value can be passed
+directly to the method through a keyword argument matching the attribute name.
+For example we can override the default password specified in the declaration
+above by simply passing in the desired password as a keyword argument to the
+factory during instantiation.
+
+.. code-block:: pycon
+
+ >>> other_u = UserFactory(password='different') # Calls user.set_password('different')
+ >>> other_u.check_password('defaultpassword')
+ False
+ >>> other_u.check_password('different')
+ True
+
+.. note::
+
+ For Django models, unless the object method called by
+ :class:`PostGenerationMethodCall` saves the object back to the
+ database, we will have to explicitly remember to save the object back
+ if we performed a ``create()``.
+
+ .. code-block:: pycon
+
+ >>> u = UserFactory.create() # u.password has not been saved back to the database
+ >>> u.save() # we must remember to do it ourselves
+
+
+ We can avoid this by subclassing from :class:`DjangoModelFactory`,
+ instead, e.g.,
+
+ .. code-block:: python
+
+ class UserFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = User
+
+ username = 'user'
+ password = factory.PostGenerationMethodCall('set_password',
+ 'defaultpassword')
+
+
+If instead the :class:`PostGenerationMethodCall` declaration uses two or
+more positional arguments, the overriding value must be an iterable. For
+example, if we declared the ``password`` attribute like the following,
+
+.. code-block:: python
+
+ class UserFactory(factory.Factory):
+ FACTORY_FOR = User
+
+ username = 'user'
+ password = factory.PostGenerationMethodCall('set_password', '', 'sha1')
+
+then we must be cautious to pass in an iterable for the ``password``
+keyword argument when creating an instance from the factory:
+
+.. code-block:: pycon
+
+ >>> UserFactory() # Calls user.set_password('', 'sha1')
+ >>> UserFactory(password=('test', 'md5')) # Calls user.set_password('test', 'md5')
+
+ >>> # Always pass in a good iterable:
+ >>> UserFactory(password=('test',)) # Calls user.set_password('test')
+ >>> UserFactory(password='test') # Calls user.set_password('t', 'e', 's', 't')
+
+
+.. note:: While this setup provides sane and intuitive defaults for most users,
+ it prevents passing more than one argument when the declaration used
+ zero or one.
+
+ In such cases, users are advised to either resort to the more powerful
+ :class:`PostGeneration` or to add the second expected argument default
+ value to the :class:`PostGenerationMethodCall` declaration
+ (``PostGenerationMethodCall('method', 'x', 'y_that_is_the_default')``)
+
+Keywords extracted from the factory arguments are merged into the
+defaults present in the :class:`PostGenerationMethodCall` declaration.
+
+.. code-block:: pycon
+
+ >>> UserFactory(password__disabled=True) # Calls user.set_password('', 'sha1', 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)
+
+ An alternate base class to :class:`Factory` can be specified in the
+ ``FACTORY_CLASS`` argument:
+
+ .. code-block:: python
+
+ UserFactory = make_factory(models.User,
+ login='john',
+ email=factory.LazyAttribute(lambda u: '%s@example.com' % u.login),
+ FACTORY_CLASS=factory.DjangoModelFactory,
+ )
+
+ # This is equivalent to:
+
+ class UserFactory(factory.DjangoModelFactory):
+ FACTORY_FOR = models.User
+
+ login = 'john'
+ email = factory.LazyAttribute(lambda u: '%s@example.com' % u.login)
+
+ .. versionadded:: 2.0.0
+ The ``FACTORY_CLASS`` kwarg was added in 2.0.0.
+
+
+Instance building
+"""""""""""""""""
+
+The :mod:`factory` module provides a bunch of shortcuts for creating a factory and
+extracting instances from them:
+
+.. function:: build(klass, FACTORY_CLASS=None, **kwargs)
+.. function:: build_batch(klass, size, FACTORY_CLASS=None, **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
+ :param FACTORY_CLASS: Alternate base class (instead of :class:`Factory`)
+
+
+
+.. function:: create(klass, FACTORY_CLASS=None, **kwargs)
+.. function:: create_batch(klass, size, FACTORY_CLASS=None, **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
+ :param FACTORY_CLASS: Alternate base class (instead of :class:`Factory`)
+
+
+
+.. function:: stub(klass, FACTORY_CLASS=None, **kwargs)
+.. function:: stub_batch(klass, size, FACTORY_CLASS=None, **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
+ :param FACTORY_CLASS: Alternate base class (instead of :class:`Factory`)
+
+
+
+.. function:: generate(klass, strategy, FACTORY_CLASS=None, **kwargs)
+.. function:: generate_batch(klass, strategy, size, FACTORY_CLASS=None, **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
+ :param FACTORY_CLASS: Alternate base class (instead of :class:`Factory`)
+
+
+
+.. function:: simple_generate(klass, create, FACTORY_CLASS=None, **kwargs)
+.. function:: simple_generate_batch(klass, create, size, FACTORY_CLASS=None, **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
+ :param FACTORY_CLASS: Alternate base class (instead of :class:`Factory`)
+
+
diff --git a/docs/subfactory.rst b/docs/subfactory.rst
deleted file mode 100644
index 1815312..0000000
--- a/docs/subfactory.rst
+++ /dev/null
@@ -1,56 +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