Skip to content

Commit

Permalink
Various improvements (#103)
Browse files Browse the repository at this point in the history
* Cleanup
* Added PreInit
* Add pre_init hook
* Add more providers
  • Loading branch information
barseghyanartur committed Jun 9, 2024
1 parent 4bfe4ed commit 5a70fea
Show file tree
Hide file tree
Showing 31 changed files with 1,198 additions and 241 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@ are used for versioning (schema follows below):
0.3.4 to 0.4).
- All backwards incompatible changes are mentioned in this document.

0.7
---
2024-06-09

.. note::

This release contains minor backwards incompatible changes. Namely,
in the ``email`` provider.

- The ``domain`` (type: ``str``, default value: ``example.com``) argument
of the ``email`` provider has been dropped in favour
of ``domain_names`` (type: ``Optional[Tuple[str]``, default value: ``None``).
- Added a dedicated ``PydanticModelFactory`` (yet equal to ``ModelFactory``)
for future improvements.
- Added ``PreInit`` factory class and ``pre_init`` decorator.
- Improved documentation of factories.
- Added ``random_choice`` and ``random_sample`` providers.
- Added ``tld``, ``domain_name``, ``free_email_domain``, ``company_email``
and ``free_email`` providers.

0.6.9
-----
2024-05-10
Expand Down
23 changes: 12 additions & 11 deletions SECURITY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,25 @@ Please report security issues by emailing Artur Barseghyan

Supported Versions
------------------
**Make sure to use the latest version.**

The two most recent ``fake.py`` release series receive security support.

For example, during the development cycle leading to the release
of ``fake.py`` 0.17.x, support will be provided for ``fake.py`` 0.16.x.

Upon the release of ``fake.py`` 0.18.x, security support for ``fake.py``
0.16.x will end.
It's recommended to use the latest version.

.. code-block:: text
┌─────────────────┬────────────────┐
│ Version │ Supported │
├─────────────────┼────────────────┤
│ 0.6.x │ Yes │
│ 0.7.x │ Yes │
├─────────────────┼────────────────┤
│ 0.5.x │ Yes │
│ 0.6.x │ Yes │
├─────────────────┼────────────────┤
│ < 0.5 │ No │
│ < 0.6 │ No │
└─────────────────┴────────────────┘
.. note::

For example, during the development cycle leading to the release
of ``fake.py`` 0.17.x, support will be provided for ``fake.py`` 0.16.x.

Upon the release of ``fake.py`` 0.18.x, security support for ``fake.py``
0.16.x will end.
2 changes: 2 additions & 0 deletions docs/_static/examples/creating_docx/docx_bytes_1.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from fake import FAKER

docx_bytes = FAKER.docx()

assert isinstance(docx_bytes, bytes)
2 changes: 2 additions & 0 deletions docs/_static/examples/creating_images/png_bytes_1.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from fake import FAKER

png_bytes = FAKER.png()

assert isinstance(png_bytes, bytes)
2 changes: 2 additions & 0 deletions docs/_static/examples/creating_pdf/text_pdf_bytes_1.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from fake import FAKER, TextPdfGenerator

pdf_bytes = FAKER.pdf(generator=TextPdfGenerator)

assert isinstance(pdf_bytes, bytes)
1 change: 1 addition & 0 deletions docs/creating_docx.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ If you need bytes

.. literalinclude:: _static/examples/creating_docx/docx_bytes_1.py
:language: python
:lines: 1-3

*See the full example*
:download:`here <_static/examples/creating_docx/docx_bytes_1.py>`
Expand Down
1 change: 1 addition & 0 deletions docs/creating_images.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ See the following full functional snippet for generating a ``PNG`` image.

.. literalinclude:: _static/examples/creating_images/png_bytes_1.py
:language: python
:lines: 1-3

*See the full example*
:download:`here <_static/examples/creating_images/png_bytes_1.py>`
Expand Down
1 change: 1 addition & 0 deletions docs/creating_pdf.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ If you need bytes

.. literalinclude:: _static/examples/creating_pdf/text_pdf_bytes_1.py
:language: python
:lines: 1-3

*See the full example*
:download:`here <_static/examples/creating_pdf/text_pdf_bytes_1.py>`
Expand Down
157 changes: 148 additions & 9 deletions docs/factories.rst
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
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.
Expand All @@ -18,30 +23,126 @@ Factories
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).
- ``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:: _static/examples/factories/django/article/models.py
:language: python
:lines: 1-3, 9-23
:lines: 1-3, 9-26

*See the full example*
:download:`here <_static/examples/factories/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:: _static/examples/factories/django/article/factories.py
:language: python
:lines: 4-5, 7, 10-11, 13, 23, 56-58, 68-72

*See the full example*
:download:`here <_static/examples/factories/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:: _static/examples/factories/django/article/factories.py
:language: python
:lines: 1-2, 8, 10, 12, 15-17, 23, 73-90, 121-135, 141-146

*See the full example*
:download:`here <_static/examples/factories/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 hot have to return anything.
- ``password`` is a non-required field and since Django has a well working way
for setting it, use of ``PreSave`` is the best option here. It's important
to mention that functions passed to the ``PreSave``, do hot 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 groups. That's why
``PostSave`` is best option here. It's important to mention that functions
passed to the ``PostSave``, do hot have to return anything.

----

Factory for the the ``Artice`` model could look as this:

*Filename: article/factories.py*

.. container:: jsphinx-download

.. literalinclude:: _static/examples/factories/django/article/factories.py
:language: python
:lines: 3, 10, 14, 18, 22-25, 35-55, 157-163, 180-198

*See the full example*
:download:`here <_static/examples/factories/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:: _static/examples/factories/django/article/factories.py
:language: python
:lines: 1-21, 31-63, 93-120, 130-132, 148-162
:lines: 1-25, 35-58, 68-90, 121-135, 141-146, 157-163, 180-199

*See the full example*
:download:`here <_static/examples/factories/django/article/factories.py>`
Expand Down Expand Up @@ -91,26 +192,35 @@ Django example

Pydantic example
----------------
Models
~~~~~~
Example Pydantic models closely resemble the earlier shown Django models.

*Filename: article/models.py*

.. container:: jsphinx-download

.. literalinclude:: _static/examples/factories/pydantic/article/models.py
:language: python
:lines: 1-5, 15-
:lines: 1-5, 15-25, 31-

*See the full example*
:download:`here <_static/examples/factories/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:: _static/examples/factories/pydantic/article/factories.py
:language: python
:lines: 1-15, 25-72, 83-98
:lines: 1-19, 29-103, 119-145

*See the full example*
:download:`here <_static/examples/factories/pydantic/article/factories.py>`
Expand All @@ -121,6 +231,10 @@ Pydantic example

TortoiseORM example
-------------------
Models
~~~~~~
Example TortoiseORM models closely resemble the earlier shown Django models.

*Filename: article/models.py*

.. container:: jsphinx-download
Expand All @@ -134,13 +248,18 @@ TortoiseORM example

----

Factories
~~~~~~~~~
Example TortoiseORM factories are almost identical to the earlier shown Django
factories.

*Filename: article/factories.py*

.. container:: jsphinx-download

.. literalinclude:: _static/examples/factories/tortoise/article/factories.py
:language: python
:lines: 1-16, 26-81, 91-106
:lines: 1-20, 30-111, 121-148

*See the full example*
:download:`here <_static/examples/factories/tortoise/article/factories.py>`
Expand All @@ -151,6 +270,10 @@ TortoiseORM example

Dataclasses example
-------------------
Models
~~~~~~
Example dataclass models closely resemble the earlier shown Django models.

*Filename: article/models.py*

.. container:: jsphinx-download
Expand All @@ -164,13 +287,18 @@ Dataclasses example

----

Factories
~~~~~~~~~
Example dataclass factories are almost identical to the earlier shown Django
factories.

*Filename: article/factories.py*

.. container:: jsphinx-download

.. literalinclude:: _static/examples/factories/dataclasses/article/factories.py
:language: python
:lines: 1-15, 25-72, 83-97
:lines: 1-19, 29-103, 114-140

*See the full example*
:download:`here <_static/examples/factories/dataclasses/article/factories.py>`
Expand All @@ -181,6 +309,8 @@ Dataclasses example

SQLAlchemy example
------------------
Configuration
~~~~~~~~~~~~~

*Filename: config.py*

Expand All @@ -195,26 +325,35 @@ SQLAlchemy example

----

Models
~~~~~~
Example SQLAlchemy models closely resemble the earlier shown Django models.

*Filename: article/models.py*

.. container:: jsphinx-download

.. literalinclude:: _static/examples/factories/sqlalchemy/article/models.py
:language: python
:lines: 1-15, 25-45, 49-74, 78-98
:lines: 1-16, 26-46, 50-75, 79-105

*See the full example*
:download:`here <_static/examples/factories/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:: _static/examples/factories/sqlalchemy/article/factories.py
:language: python
:lines: 1-16, 25-96, 107-125
:lines: 1-20, 29-127, 138-168

*See the full example*
:download:`here <_static/examples/factories/sqlalchemy/article/factories.py>`
Expand Down
Loading

0 comments on commit 5a70fea

Please sign in to comment.