summaryrefslogtreecommitdiff
path: root/docs/reference.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/reference.rst')
-rw-r--r--docs/reference.rst335
1 files changed, 268 insertions, 67 deletions
diff --git a/docs/reference.rst b/docs/reference.rst
index 53584a0..b5ccd16 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -11,37 +11,54 @@ For internals and customization points, please refer to the :doc:`internals` sec
The :class:`Factory` class
--------------------------
-.. class:: Factory
+.. class:: FactoryOptions
+
+ .. versionadded:: 2.4.0
+
+ A :class:`Factory`'s behaviour can be tuned through a few settings.
- The :class:`Factory` class is the base of factory_boy features.
+ For convenience, they are declared in a single ``class Meta`` attribute:
- It accepts a few specific attributes (must be specified on class declaration):
+ .. code-block:: python
+
+ class MyFactory(factory.Factory):
+ class Meta:
+ model = MyObject
+ abstract = False
- .. attribute:: FACTORY_FOR
+ .. attribute:: model
This optional attribute describes the class of objects to generate.
If unset, it will be inherited from parent :class:`Factory` subclasses.
- .. attribute:: ABSTRACT_FACTORY
+ .. versionadded:: 2.4.0
+
+ .. attribute:: abstract
This attribute indicates that the :class:`Factory` subclass should not
be used to generate objects, but instead provides some extra defaults.
It will be automatically set to ``True`` if neither the :class:`Factory`
- subclass nor its parents define the :attr:`~Factory.FACTORY_FOR` attribute.
+ subclass nor its parents define the :attr:`~FactoryOptions.model` attribute.
- .. attribute:: FACTORY_ARG_PARAMETERS
+ .. warning:: This flag is reset to ``False`` when a :class:`Factory` subclasses
+ another one if a :attr:`~FactoryOptions.model` is set.
+
+ .. versionadded:: 2.4.0
+
+ .. attribute:: inline_args
Some factories require non-keyword arguments to their :meth:`~object.__init__`.
- They should be listed, in order, in the :attr:`FACTORY_ARG_PARAMETERS`
+ They should be listed, in order, in the :attr:`inline_args`
attribute:
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
- FACTORY_ARG_PARAMETERS = ('login', 'email')
+ class Meta:
+ model = User
+ inline_args = ('login', 'email')
login = 'john'
email = factory.LazyAttribute(lambda o: '%s@example.com' % o.login)
@@ -53,22 +70,25 @@ The :class:`Factory` class
<User: john>
>>> User('john', 'john@example.com', firstname="John") # actual call
- .. attribute:: FACTORY_HIDDEN_ARGS
+ .. versionadded:: 2.4.0
+
+ .. attribute:: exclude
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
+ passed to the model 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
+ Factory fields whose name are listed in :attr:`exclude` 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',)
+ class Meta:
+ model = Order
+ exclude = ('now',)
now = factory.LazyAttribute(lambda o: datetime.datetime.utcnow())
started_at = factory.LazyAttribute(lambda o: o.now - datetime.timedelta(hours=1))
@@ -83,6 +103,60 @@ The :class:`Factory` class
>>> OrderFactory(now=datetime.datetime(2013, 4, 1, 10))
<Order: started 2013-04-01 09:00:00, paid 2013-04-01 09:10:00>
+ .. versionadded:: 2.4.0
+
+
+ .. attribute:: rename
+
+ Sometimes, a model expect a field with a name already used by one
+ of :class:`Factory`'s methods.
+
+ In this case, the :attr:`rename` attributes allows to define renaming
+ rules: the keys of the :attr:`rename` dict are those used in the
+ :class:`Factory` declarations, and their values the new name:
+
+ .. code-block:: python
+
+ class ImageFactory(factory.Factory):
+ # The model expects "attributes"
+ form_attributes = ['thumbnail', 'black-and-white']
+
+ class Meta:
+ model = Image
+ rename = {'form_attributes': 'attributes'}
+
+ .. versionadded: 2.6.0
+
+
+ .. attribute:: strategy
+
+ Use this attribute to change the strategy used by a :class:`Factory`.
+ The default is :data:`CREATE_STRATEGY`.
+
+
+
+.. class:: Factory
+
+
+ **Class-level attributes:**
+
+ .. attribute:: _meta
+
+ .. versionadded:: 2.4.0
+
+ The :class:`FactoryOptions` instance attached to a :class:`Factory` class is available
+ as a :attr:`_meta` attribute.
+
+ .. attribute:: _options_class
+
+ .. versionadded:: 2.4.0
+
+ If a :class:`Factory` subclass needs to define additional, extra options, it has to
+ provide a custom :class:`FactoryOptions` subclass.
+
+ A pointer to that custom class should be provided as :attr:`_options_class` so that
+ the :class:`Factory`-building metaclass can use it instead.
+
**Base functions:**
@@ -162,7 +236,7 @@ The :class:`Factory` class
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
+ items removed, but before the :attr:`~FactoryOptions.inline_args` extraction
phase.
.. code-block:: python
@@ -177,7 +251,6 @@ The :class:`Factory` class
.. OHAI_VIM**
-
.. classmethod:: _setup_next_sequence(cls)
This method will compute the first value to use for the sequence counter
@@ -189,19 +262,19 @@ The :class:`Factory` class
Subclasses may fetch the next free ID from the database, for instance.
- .. classmethod:: _build(cls, target_class, *args, **kwargs)
+ .. classmethod:: _build(cls, model_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
+ It receives the model class (provided to :attr:`~FactoryOptions.model`), 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)
+ .. classmethod:: _create(cls, model_class, *args, **kwargs)
.. OHAI_VIM*
@@ -214,10 +287,11 @@ The :class:`Factory` class
.. code-block:: python
class BaseBackendFactory(factory.Factory):
- ABSTRACT_FACTORY = True # Optional
+ class Meta:
+ abstract = True # Optional
- def _create(cls, target_class, *args, **kwargs):
- obj = target_class(*args, **kwargs)
+ def _create(cls, model_class, *args, **kwargs):
+ obj = model_class(*args, **kwargs)
obj.save()
return obj
@@ -254,7 +328,7 @@ The :class:`Factory` class
>>> SomeFactory._next_sequence
4
- Since subclasses of a non-:attr:`abstract <factory.Factory.ABSTRACT_FACTORY>`
+ Since subclasses of a non-:attr:`abstract <factory.FactoryOptions.abstract>`
:class:`~factory.Factory` share the same sequence counter, special care needs
to be taken when resetting the counter of such a subclass.
@@ -293,7 +367,7 @@ factory_boy supports two main strategies for generating instances, plus stubs.
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.
+ :attr:`~FactoryOptions.model` class.
.. data:: CREATE_STRATEGY
@@ -316,7 +390,7 @@ factory_boy supports two main strategies for generating instances, plus stubs.
when using the ``create`` strategy.
That policy will be used if the
- :attr:`associated class <Factory.FACTORY_FOR>` has an ``objects``
+ :attr:`associated class <FactoryOptions.model>` has an ``objects``
attribute *and* the :meth:`~Factory._create` classmethod of the
:class:`Factory` wasn't overridden.
@@ -337,7 +411,7 @@ factory_boy supports two main strategies for generating instances, plus stubs.
.. 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
+ an instance of the :attr:`~FactoryOptions.model` class, and actually doesn't
require one to be present.
Instead, it returns an instance of :class:`StubObject` whose attributes have been
@@ -359,7 +433,7 @@ factory_boy supports two main strategies for generating instances, plus stubs.
.. class:: StubFactory(Factory)
- An :attr:`abstract <Factory.ABSTRACT_FACTORY>` :class:`Factory`,
+ An :attr:`abstract <FactoryOptions.abstract>` :class:`Factory`,
with a default strategy set to :data:`STUB_STRATEGY`.
@@ -400,6 +474,83 @@ factory_boy supports two main strategies for generating instances, plus stubs.
Declarations
------------
+
+Faker
+"""""
+
+.. class:: Faker(provider, locale=None, **kwargs)
+
+ .. OHAIVIM**
+
+ In order to easily define realistic-looking factories,
+ use the :class:`Faker` attribute declaration.
+
+ This is a wrapper around `fake-factory <https://pypi.python.org/pypi/fake-factory>`_;
+ its argument is the name of a ``fake-factory`` provider:
+
+ .. code-block:: python
+
+ class UserFactory(factory.Factory):
+ class Meta:
+ model = User
+
+ name = factory.Faker('name')
+
+ .. code-block:: pycon
+
+ >>> user = UserFactory()
+ >>> user.name
+ 'Lucy Cechtelar'
+
+
+ .. attribute:: locale
+
+ If a custom locale is required for one specific field,
+ use the ``locale`` parameter:
+
+ .. code-block:: python
+
+ class UserFactory(factory.Factory):
+ class Meta:
+ model = User
+
+ name = factory.Faker('name', locale='fr_FR')
+
+ .. code-block:: pycon
+
+ >>> user = UserFactory()
+ >>> user.name
+ 'Jean Valjean'
+
+
+ .. classmethod:: override_default_locale(cls, locale)
+
+ If the locale needs to be overridden for a whole test,
+ use :meth:`~factory.Faker.override_default_locale`:
+
+ .. code-block:: pycon
+
+ >>> with factory.Faker.override_default_locale('de_DE'):
+ ... UserFactory()
+ <User: Johannes Brahms>
+
+ .. classmethod:: add_provider(cls, locale=None)
+
+ Some projects may need to fake fields beyond those provided by ``fake-factory``;
+ in such cases, use :meth:`factory.Faker.add_provider` to declare additional providers
+ for those fields:
+
+ .. code-block:: python
+
+ factory.Faker.add_provider(SmileyProvider)
+
+ class FaceFactory(factory.Factory):
+ class Meta:
+ model = Face
+
+ smiley = factory.Faker('smiley')
+
+
LazyAttribute
"""""""""""""
@@ -414,7 +565,8 @@ accept the object being built as sole argument, and return a value.
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
username = 'john'
email = factory.LazyAttribute(lambda o: '%s@example.com' % o.username)
@@ -449,7 +601,8 @@ return value of the method:
.. code-block:: python
class UserFactory(factory.Factory)
- FACTORY_FOR = User
+ class Meta:
+ model = User
name = u"Jean"
@@ -481,13 +634,14 @@ This declaration takes a single argument, a function accepting a single paramete
.. 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.
+ This feature was deprecated in 1.3.0 and will be removed in 2.0.0.
.. code-block:: python
class UserFactory(factory.Factory)
- FACTORY_FOR = User
+ class Meta:
+ model = User
phone = factory.Sequence(lambda n: '123-555-%04d' % n)
@@ -512,7 +666,8 @@ be the sequence counter - this might be confusing:
.. code-block:: python
class UserFactory(factory.Factory)
- FACTORY_FOR = User
+ class Meta:
+ model = User
@factory.sequence
def phone(n):
@@ -537,7 +692,8 @@ The sequence counter is shared across all :class:`Sequence` attributes of the
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
phone = factory.Sequence(lambda n: '%04d' % n)
office = factory.Sequence(lambda n: 'A23-B%03d' % n)
@@ -561,7 +717,8 @@ sequence counter is shared:
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
phone = factory.Sequence(lambda n: '123-555-%04d' % n)
@@ -596,7 +753,8 @@ class-level value.
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
uid = factory.Sequence(int)
@@ -631,7 +789,8 @@ It takes a single argument, a function whose two parameters are, in order:
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
login = 'john'
email = factory.LazyAttributeSequence(lambda o, n: '%s@s%d.example.com' % (o.login, n))
@@ -655,7 +814,8 @@ handles more complex cases:
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
login = 'john'
@@ -692,7 +852,8 @@ The :class:`SubFactory` attribute should be called with:
.. code-block:: python
class FooFactory(factory.Factory):
- FACTORY_FOR = Foo
+ class Meta:
+ model = Foo
bar = factory.SubFactory(BarFactory) # Not BarFactory()
@@ -705,7 +866,8 @@ Definition
# A standard factory
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
# Various fields
first_name = 'John'
@@ -714,7 +876,8 @@ Definition
# A factory for an object with a 'User' field
class CompanyFactory(factory.Factory):
- FACTORY_FOR = Company
+ class Meta:
+ model = Company
name = factory.Sequence(lambda n: 'FactoryBoyz' + 'z' * n)
@@ -794,13 +957,15 @@ This issue can be handled by passing the absolute import path to the target
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
username = 'john'
main_group = factory.SubFactory('users.factories.GroupFactory')
class GroupFactory(factory.Factory):
- FACTORY_FOR = Group
+ class Meta:
+ model = Group
name = "MyGroup"
owner = factory.SubFactory(UserFactory)
@@ -828,7 +993,8 @@ That declaration takes a single argument, a dot-delimited path to the attribute
.. code-block:: python
class UserFactory(factory.Factory)
- FACTORY_FOR = User
+ class Meta:
+ model = User
birthdate = factory.Sequence(lambda n: datetime.date(2000, 1, 1) + datetime.timedelta(days=n))
birthmonth = factory.SelfAttribute('birthdate.month')
@@ -854,13 +1020,15 @@ gains an "upward" semantic through the double-dot notation, as used in Python im
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
language = 'en'
class CompanyFactory(factory.Factory):
- FACTORY_FOR = Company
+ class Meta:
+ model = Company
country = factory.SubFactory(CountryFactory)
owner = factory.SubFactory(UserFactory, language=factory.SelfAttribute('..country.language'))
@@ -888,7 +1056,8 @@ through the :attr:`~containers.LazyStub.factory_parent` attribute of the passed-
.. code-block:: python
class CompanyFactory(factory.Factory):
- FACTORY_FOR = Company
+ class Meta:
+ model = Company
country = factory.SubFactory(CountryFactory)
owner = factory.SubFactory(UserFactory,
language=factory.LazyAttribute(lambda user: user.factory_parent.country.language),
@@ -966,7 +1135,8 @@ adequate value.
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
# CATEGORY_CHOICES is a list of (key, title) tuples
category = factory.Iterator(User.CATEGORY_CHOICES, getter=lambda c: c[0])
@@ -987,7 +1157,8 @@ use the :func:`iterator` decorator:
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
@factory.iterator
def name():
@@ -1030,7 +1201,8 @@ with the :class:`Dict` and :class:`List` attributes:
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
is_superuser = False
roles = factory.Dict({
@@ -1066,7 +1238,8 @@ with the :class:`Dict` and :class:`List` attributes:
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
flags = factory.List([
'user',
@@ -1113,10 +1286,11 @@ For instance, a :class:`PostGeneration` hook is declared as ``post``:
.. code-block:: python
class SomeFactory(factory.Factory):
- FACTORY_FOR = SomeObject
+ class Meta:
+ model = SomeObject
@post_generation
- def post(self, create, extracted, **kwargs):
+ def post(obj, create, extracted, **kwargs):
obj.set_origin(create)
.. OHAI_VIM**
@@ -1128,7 +1302,7 @@ When calling the factory, some arguments will be extracted for this method:
- 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.
+Extracted arguments won't be passed to the :attr:`~FactoryOptions.model` class.
Thus, in the following call:
@@ -1142,7 +1316,7 @@ Thus, in the following call:
)
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``.
+as keyword arguments; ``{'post_x': 2}`` will be passed to ``SomeFactory._meta.model``.
RelatedFactory
@@ -1184,7 +1358,8 @@ RelatedFactory
.. code-block:: python
class FooFactory(factory.Factory):
- FACTORY_FOR = Foo
+ class Meta:
+ model = Foo
bar = factory.RelatedFactory(BarFactory) # Not BarFactory()
@@ -1192,13 +1367,15 @@ RelatedFactory
.. code-block:: python
class CityFactory(factory.Factory):
- FACTORY_FOR = City
+ class Meta:
+ model = City
capital_of = None
name = "Toronto"
class CountryFactory(factory.Factory):
- FACTORY_FOR = Country
+ class Meta:
+ model = Country
lang = 'fr'
capital_city = factory.RelatedFactory(CityFactory, 'capital_of', name="Paris")
@@ -1235,12 +1412,29 @@ If a value if passed for the :class:`RelatedFactory` attribute, this disables
1
+.. note:: The target of the :class:`RelatedFactory` is evaluated *after* the initial factory has been instantiated.
+ This means that calls to :class:`factory.SelfAttribute` cannot go higher than this :class:`RelatedFactory`:
+
+ .. code-block:: python
+
+ class CountryFactory(factory.Factory):
+ class Meta:
+ model = Country
+
+ lang = 'fr'
+ capital_city = factory.RelatedFactory(CityFactory, 'capital_of',
+ # factory.SelfAttribute('..lang') will crash, since the context of
+ # ``CountryFactory`` has already been evaluated.
+ main_lang=factory.SelfAttribute('capital_of.lang'),
+ )
+
+
PostGeneration
""""""""""""""
.. class:: PostGeneration(callable)
-The :class:`PostGeneration` declaration performs actions once the target object
+The :class:`PostGeneration` declaration performs actions once the model object
has been generated.
Its sole argument is a callable, that will be called once the base object has
@@ -1260,7 +1454,8 @@ as ``callable(obj, create, extracted, **kwargs)``, where:
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
login = 'john'
make_mbox = factory.PostGeneration(
@@ -1280,7 +1475,8 @@ A decorator is also provided, decorating a single method accepting the same
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
login = 'john'
@@ -1316,7 +1512,7 @@ PostGenerationMethodCall
.. attribute:: method_name
- The name of the method to call on the :attr:`~Factory.FACTORY_FOR` object
+ The name of the method to call on the :attr:`~FactoryOptions.model` object
.. attribute:: args
@@ -1340,7 +1536,8 @@ attribute like below:
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
username = 'user'
password = factory.PostGenerationMethodCall('set_password',
@@ -1390,7 +1587,8 @@ factory during instantiation.
.. code-block:: python
class UserFactory(factory.django.DjangoModelFactory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
username = 'user'
password = factory.PostGenerationMethodCall('set_password',
@@ -1404,7 +1602,8 @@ example, if we declared the ``password`` attribute like the following,
.. code-block:: python
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
username = 'user'
password = factory.PostGenerationMethodCall('set_password', '', 'sha1')
@@ -1467,7 +1666,8 @@ Lightweight factory declaration
# This is equivalent to:
class UserFactory(factory.Factory):
- FACTORY_FOR = User
+ class Meta:
+ model = User
login = 'john'
email = factory.LazyAttribute(lambda u: '%s@example.com' % u.login)
@@ -1486,7 +1686,8 @@ Lightweight factory declaration
# This is equivalent to:
class UserFactory(factory.django.DjangoModelFactory):
- FACTORY_FOR = models.User
+ class Meta:
+ model = models.User
login = 'john'
email = factory.LazyAttribute(lambda u: '%s@example.com' % u.login)