aboutsummaryrefslogtreecommitdiff
path: root/docs/introduction.rst
diff options
context:
space:
mode:
Diffstat (limited to 'docs/introduction.rst')
-rw-r--r--docs/introduction.rst258
1 files changed, 258 insertions, 0 deletions
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.
+
+