diff options
Diffstat (limited to 'docs/recipes.rst')
-rw-r--r-- | docs/recipes.rst | 129 |
1 files changed, 123 insertions, 6 deletions
diff --git a/docs/recipes.rst b/docs/recipes.rst index 72dacef..df86bac 100644 --- a/docs/recipes.rst +++ b/docs/recipes.rst @@ -33,6 +33,29 @@ use the :class:`~factory.SubFactory` declaration: group = factory.SubFactory(GroupFactory) +Choosing from a populated table +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the target of the :class:`~django.db.models.ForeignKey` should be +chosen from a pre-populated table +(e.g :class:`django.contrib.contenttypes.models.ContentType`), +simply use a :class:`factory.Iterator` on the chosen queryset: + +.. code-block:: python + + import factory, factory.django + from . import models + + class UserFactory(factory.django.DjangoModelFactory): + class Meta: + model = models.User + + language = factory.Iterator(models.Language.objects.all()) + +Here, ``models.Language.objects.all()`` won't be evaluated until the +first call to ``UserFactory``; thus avoiding DB queries at import time. + + Reverse dependencies (reverse ForeignKey) ----------------------------------------- @@ -125,8 +148,8 @@ factory_boy related factories. method. -Simple ManyToMany ------------------ +Simple Many-to-many relationship +-------------------------------- 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` @@ -144,7 +167,7 @@ hook: class User(models.Model): name = models.CharField() - groups = models.ManyToMany(Group) + groups = models.ManyToManyField(Group) # factories.py @@ -181,8 +204,8 @@ the ``groups`` declaration will add passed in groups to the set of groups for th user. -ManyToMany with a 'through' ---------------------------- +Many-to-many relation with a 'through' +-------------------------------------- If only one link is required, this can be simply performed with a :class:`RelatedFactory`. @@ -196,7 +219,7 @@ If more links are needed, simply add more :class:`RelatedFactory` declarations: class Group(models.Model): name = models.CharField() - members = models.ManyToMany(User, through='GroupLevel') + members = models.ManyToManyField(User, through='GroupLevel') class GroupLevel(models.Model): user = models.ForeignKey(User) @@ -327,3 +350,97 @@ default :meth:`Model.objects.create() <django.db.models.query.QuerySet.create>` manager = cls._get_manager(model_class) # The default would use ``manager.create(*args, **kwargs)`` return manager.create_user(*args, **kwargs) + + +Forcing the sequence counter +---------------------------- + +A common pattern with factory_boy is to use a :class:`factory.Sequence` declaration +to provide varying values to attributes declared as unique. + +However, it is sometimes useful to force a given value to the counter, for instance +to ensure that tests are properly reproductible. + +factory_boy provides a few hooks for this: + + +Forcing the value on a per-call basis + In order to force the counter for a specific :class:`~factory.Factory` instantiation, + just pass the value in the ``__sequence=42`` parameter: + + .. code-block:: python + + class AccountFactory(factory.Factory): + class Meta: + model = Account + uid = factory.Sequence(lambda n: n) + name = "Test" + + .. code-block:: pycon + + >>> obj1 = AccountFactory(name="John Doe", __sequence=10) + >>> obj1.uid # Taken from the __sequence counter + 10 + >>> obj2 = AccountFactory(name="Jane Doe") + >>> obj2.uid # The base sequence counter hasn't changed + 1 + + +Resetting the counter globally + If all calls for a factory must start from a deterministic number, + use :meth:`factory.Factory.reset_sequence`; this will reset the counter + to its initial value (as defined by :meth:`factory.Factory._setup_next_sequence`). + + .. code-block:: pycon + + >>> AccountFactory().uid + 1 + >>> AccountFactory().uid + 2 + >>> AccountFactory.reset_sequence() + >>> AccountFactory().uid # Reset to the initial value + 1 + >>> AccountFactory().uid + 2 + + It is also possible to reset the counter to a specific value: + + .. code-block:: pycon + + >>> AccountFactory.reset_sequence(10) + >>> AccountFactory().uid + 10 + >>> AccountFactory().uid + 11 + + This recipe is most useful in a :class:`~unittest.TestCase`'s + :meth:`~unittest.TestCase.setUp` method. + + +Forcing the initial value for all projects + The sequence counter of a :class:`~factory.Factory` can also be set + automatically upon the first call through the + :meth:`~factory.Factory._setup_next_sequence` method; this helps when the + objects's attributes mustn't conflict with pre-existing data. + + A typical example is to ensure that running a Python script twice will create + non-conflicting objects, by setting up the counter to "max used value plus one": + + .. code-block:: python + + class AccountFactory(factory.django.DjangoModelFactory): + class Meta: + model = models.Account + + @classmethod + def _setup_next_sequence(cls): + try: + return models.Accounts.objects.latest('uid').uid + 1 + except models.Account.DoesNotExist: + return 1 + + .. code-block:: pycon + + >>> Account.objects.create(uid=42, name="Blah") + >>> AccountFactory.create() # Sets up the account number based on the latest uid + <Account uid=43, name=Test> |