Factories ========= - ``pre_init`` is a method decorator that will always run before the instance is initialised. - ``pre_save`` is a method decorator that will always run before the instance is saved. - ``post_save`` is a method decorator that will always run after the instance is saved. - ``trait`` decorator runs the code if set to True in factory constructor. - ``PreInit`` is like the ``pre_init`` decorator of the ``ModelFactory``, but you can pass arguments to it and have a lot of flexibility. See a working example (below) of how set a user password in Django. - ``PreSave`` is like the ``pre_save`` decorator of the ``ModelFactory``, but you can pass arguments to it and have a lot of flexibility. See a working example (below) of how set a user password in Django. - ``PostSave`` is like the ``post_save`` decorator of the ``ModelFactory``, but you can pass arguments to it and have a lot of flexibility. See a working example (below) of how to assign a User to a Group after User creation. - ``LazyAttribute`` expects a callable, will take the instance as a first argument, runs it with extra arguments specified and sets the value as an attribute name. - ``LazyFunction`` expects a callable, runs it (without any arguments) and sets the value as an attribute name. - ``SubFactory`` is for specifying relations (typically - ForeignKeys or nested objects). Django example -------------- Models ~~~~~~ In the ``Django`` example, we will be using ``User`` and ``Group`` models from ``django.contrib.auth`` sub-package. The ``Article`` would be the only application specific custom model. *Filename: article/models.py* .. container:: jsphinx-download .. literalinclude:: ../examples/django/article/models.py :language: python :lines: 1-3, 9-26 *See the full example* :download:`here <../examples/django/article/models.py>` ---- Factories ~~~~~~~~~ Factory for the Django's built-in ``Group`` model could look as simple as this: *Filename: article/factories.py* .. container:: jsphinx-download .. literalinclude:: ../examples/django/article/factories.py :language: python :lines: 4-5, 7, 10-11, 13, 23, 56-58, 68-72 *See the full example* :download:`here <../examples/django/article/factories.py>` ---- Factory for the Django's built-in ``User`` model could look as this: *Filename: article/factories.py* .. container:: jsphinx-download .. literalinclude:: ../examples/django/article/factories.py :language: python :lines: 1-2, 4, 6-10, 12, 15-17, 23, 73-90, 121-135, 141-146 *See the full example* :download:`here <../examples/django/article/factories.py>` Breakdown: - ``username`` is a required field. We shouldn't be using ``PreSave`` or ``PostSave`` methods here, because we need it to be available and resolved before calling the class constructor (missing required fields would fail on Pydantic and other frameworks that enforce strong type checking). That's why ``PreInit``, which operates on the ``dict`` level, from which the model instance is constructed, is used here to construct the ``username`` value from ``first_name`` and the ``last_name``. The ``set_username`` helper function, which is used by ``PreInit``, accepts a dictionary with model data as argument and all changes to that dictionary are passed further to the class constructor. It's important to mention that functions passed to the ``PreInit``, do not have to return anything. - ``password`` is a non-required field and since Django provides a built-in way to handle passwords, use of ``PreSave`` is the best option here. It's important to mention that functions passed to the ``PreSave``, do not have to return anything. - ``group`` is a non-required many-to-many relationship. We need a User instance to be created before we can add User to a Group. That's why ``PostSave`` is the best option here. It's important to mention that functions passed to the ``PostSave``, do not have to return anything. ---- A factory for the ``Article`` model could look like this: *Filename: article/factories.py* .. container:: jsphinx-download .. literalinclude:: ../examples/django/article/factories.py :language: python :lines: 3, 10, 14, 18, 20, 23-25, 35-55, 157-163, 180-198 *See the full example* :download:`here <../examples/django/article/factories.py>` Breakdown: - ``headline`` is a required field that should be available and resolved before the class constructor is called. We already know that ``PreInit`` should be used for such cases. The ``headline`` value is constructed from ``content``. - ``author`` is a foreign key relation field to the ``User`` model. For foreign key relations ``SubFactory`` is our best choice. - ``image`` is a file field. Files created shall be placed in the path specified in ``MEDIA_ROOT`` Django setting. That's why we create and configure the ``STORAGE`` instance to pass it to ``FACTORY.png_file`` in a ``storage`` argument. - ``auto_minutes_to_read`` is a required field of the ``Article`` model. It needs to be resolved and available before the constructor class is called. That's the ``@pre_init`` decorator is used on the ``set_auto_minutes_read`` helper method. ---- All together it would look as follows: *Filename: article/factories.py* .. container:: jsphinx-download .. literalinclude:: ../examples/django/article/factories.py :language: python :lines: 1-25, 35-58, 68-90, 121-135, 141-146, 157-163, 180-199 *See the full example* :download:`here <../examples/django/article/factories.py>` ---- **Usage example** .. code-block:: python # Create one article article = ArticleFactory() # Create 5 articles articles = ArticleFactory.create_batch(5) # Create one article with authors username set to admin. article = ArticleFactory(author__username="admin") # Using trait user = UserFactory(is_admin_user=True) # Using trait in SubFactory article = ArticleFactory(author__is_admin_user=True) # Create a user. Created user will automatically have his password # set to "test1234" and will be added to the group "Test group". user = UserFactory() # Create a user with custom password user = UserFactory( password=PreSave(set_password, password="another-pass"), ) # Add a user to another group user = UserFactory( group=PostSave(add_to_group, name="Another group"), ) # Or even add user to multiple groups at once user = UserFactory( group_1=PostSave(add_to_group, name="Another group"), group_2=PostSave(add_to_group, name="Yet another group"), ) ---- Pydantic example ---------------- Models ~~~~~~ Example Pydantic models closely resemble the earlier shown Django models. *Filename: article/models.py* .. container:: jsphinx-download .. literalinclude:: ../examples/pydantic/article/models.py :language: python :lines: 1-5, 15-25, 31- *See the full example* :download:`here <../examples/pydantic/article/models.py>` ---- Factories ~~~~~~~~~ Example Pydantic factories are almost identical to the earlier shown Django factories. *Filename: article/factories.py* .. container:: jsphinx-download .. literalinclude:: ../examples/pydantic/article/factories.py :language: python :lines: 1-20, 30-98, 114-140 *See the full example* :download:`here <../examples/pydantic/article/factories.py>` *Used just like in previous example.* ---- TortoiseORM example ------------------- .. note:: TortoiseORM introduces deadlocks from version to version. Currently, last version that worked smoothly with factories of this package was 0.22.2. 0.22.x, 0.20.x and 0.21.x branches worked, while 0.23.x and 0.24.x fail. Models ~~~~~~ Example TortoiseORM models closely resemble the earlier shown Django models. *Filename: article/models.py* .. container:: jsphinx-download .. literalinclude:: ../examples/tortoise/article/models.py :language: python :lines: 1-5, 15-21, 25-41, 45-61 *See the full example* :download:`here <../examples/tortoise/article/models.py>` ---- Factories ~~~~~~~~~ Example TortoiseORM factories are almost identical to the earlier shown Django factories. *Filename: article/factories.py* .. container:: jsphinx-download .. literalinclude:: ../examples/tortoise/article/factories.py :language: python :lines: 1-21, 31-106, 116-143 *See the full example* :download:`here <../examples/tortoise/article/factories.py>` *Used just like in previous example.* ---- Dataclasses example ------------------- Models ~~~~~~ Example dataclass models closely resemble the earlier shown Django models. *Filename: article/models.py* .. container:: jsphinx-download .. literalinclude:: ../examples/dataclasses/article/models.py :language: python :lines: 1-5, 15- *See the full example* :download:`here <../examples/dataclasses/article/models.py>` ---- Factories ~~~~~~~~~ Example dataclass factories are almost identical to the earlier shown Django factories. *Filename: article/factories.py* .. container:: jsphinx-download .. literalinclude:: ../examples/dataclasses/article/factories.py :language: python :lines: 1-20, 30-98, 109-135 *See the full example* :download:`here <../examples/dataclasses/article/factories.py>` *Used just like in previous example.* ---- SQLAlchemy example ------------------ Configuration ~~~~~~~~~~~~~ *Filename: config.py* .. container:: jsphinx-download .. literalinclude:: ../examples/sqlalchemy/config.py :language: python :lines: 1-2, 12- *See the full example* :download:`here <../examples/sqlalchemy/config.py>` ---- Models ~~~~~~ Example SQLAlchemy models closely resemble the earlier shown Django models. *Filename: article/models.py* .. container:: jsphinx-download .. literalinclude:: ../examples/sqlalchemy/article/models.py :language: python :lines: 1-16, 26-46, 50-75, 79-105 *See the full example* :download:`here <../examples/sqlalchemy/article/models.py>` ---- Factories ~~~~~~~~~ Example SQLAlchemy factories are almost identical to the earlier shown Django factories. *Filename: article/factories.py* .. container:: jsphinx-download .. literalinclude:: ../examples/sqlalchemy/article/factories.py :language: python :lines: 1-21, 30-122, 133-163 *See the full example* :download:`here <../examples/sqlalchemy/article/factories.py>` *Used just like in previous example.* ---- SQLModel example ---------------- Configuration ~~~~~~~~~~~~~ *Filename: config.py* .. container:: jsphinx-download .. literalinclude:: ../examples/sqlmodel/config.py :language: python :lines: 1-2, 12- *See the full example* :download:`here <../examples/sqlmodel/config.py>` ---- Models ~~~~~~ Example SQLModel models closely resemble the earlier shown Django models. *Filename: article/models.py* .. container:: jsphinx-download .. literalinclude:: ../examples/sqlmodel/article/models.py :language: python :lines: 1-5, 15- *See the full example* :download:`here <../examples/sqlmodel/article/models.py>` ---- Factories ~~~~~~~~~ Example SQLModel factories are identical to the earlier shown SQLAlchemy factories. *Filename: article/factories.py* .. container:: jsphinx-download .. literalinclude:: ../examples/sqlmodel/article/factories.py :language: python :lines: 1-21, 31- *See the full example* :download:`here <../examples/sqlmodel/article/factories.py>` *Used just like in previous example.* ---- .. raw:: html