Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ build/
lib64
pyvenv.cfg
dist/
.project
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased](https://github.com/model-bakers/model_bakery/tree/main)

### Added
- [dev] Adds support to use values generated by [Faker](https://pypi.org/project/Faker/) [#365](https://github.com/model-bakers/model_bakery/pull/365)

### Changed
- [dev] Switch to Python 3.11 release in CI [#357](https://github.com/model-bakers/model_bakery/pull/357)
Expand Down
23 changes: 23 additions & 0 deletions docs/source/how_bakery_behaves.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,29 @@ Additionaly, if you want to your created instance to be returned respecting one
movie = baker.make(Movie, title='Old Boys', _from_manager='availables') # This will use the Movie.availables model manager


Also passing ``_use_faker_generator=True`` will make ``baker`` to use `Faker <https://pypi.org/project/Faker/>`_ to generate values. ``baker`` will read the field name and then select a generator from there. Currently we only support the following fields, however this list will increase:

- ``username``
- ``email``
- ``first_name``
- ``last_name``
- ``name``
- ``fullname``
- ``full_name``
- ``ip``
- ``ipv4``
- ``ipv6``

Examples:

.. code-block:: python

profile = baker.make(
models.Profile,
_use_faker_generator=True,
)
print(profile.name) # would print a more realistic fake email, for example: 'Erik Barnett'

Save method custom parameters
-----------------------------

Expand Down
27 changes: 24 additions & 3 deletions model_bakery/baker.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
ModelNotFound,
RecipeIteratorEmpty,
)
from .faker_gen import faker_generator_mapping
from .utils import seq # NoQA: enable seq to be imported from baker
from .utils import import_from_str

Expand All @@ -69,6 +70,7 @@ def make(
_create_files: bool = False,
_using: str = "",
_bulk_create: bool = False,
_use_faker_generator: Optional[bool] = False,
**attrs: Any,
) -> M:
...
Expand All @@ -85,6 +87,7 @@ def make(
_using: str = "",
_bulk_create: bool = False,
_fill_optional: Union[List[str], bool] = False,
_use_faker_generator: Optional[bool] = False,
**attrs: Any,
) -> List[M]:
...
Expand All @@ -100,17 +103,22 @@ def make(
_using: str = "",
_bulk_create: bool = False,
_fill_optional: Union[List[str], bool] = False,
_use_faker_generator: Optional[bool] = False,
**attrs: Any,
):
"""Create a persisted instance from a given model its associated models.

It fill the fields with random values or you can specify which
It fills the fields with random values or you can specify which
fields you want to define its values by yourself.
"""
_save_kwargs = _save_kwargs or {}
attrs.update({"_fill_optional": _fill_optional})
baker: Baker = Baker.create(
_model, make_m2m=make_m2m, create_files=_create_files, _using=_using
_model,
make_m2m=make_m2m,
create_files=_create_files,
_using=_using,
_use_faker_generator=_use_faker_generator,
)
if _valid_quantity(_quantity):
raise InvalidQuantityException
Expand Down Expand Up @@ -327,11 +335,16 @@ def create(
make_m2m: bool = False,
create_files: bool = False,
_using: str = "",
_use_faker_generator: Optional[bool] = False,
) -> "Baker[NewM]":
"""Create the baker class defined by the `BAKER_CUSTOM_CLASS` setting."""
baker_class = _custom_baker_class() or cls
return cast(Type[Baker[NewM]], baker_class)(
_model, make_m2m, create_files, _using=_using
_model,
make_m2m,
create_files,
_using=_using,
_use_faker_generator=_use_faker_generator,
)

def __init__(
Expand All @@ -340,6 +353,7 @@ def __init__(
make_m2m: bool = False,
create_files: bool = False,
_using: str = "",
_use_faker_generator: Optional[bool] = False,
) -> None:
self.make_m2m = make_m2m
self.create_files = create_files
Expand All @@ -349,6 +363,7 @@ def __init__(
self.rel_attrs: Dict[str, Any] = {}
self.rel_fields: List[str] = []
self._using = _using
self._use_faker_generator = _use_faker_generator

if isinstance(_model, str):
self.model = cast(Type[M], self.finder.get_model(_model))
Expand All @@ -371,6 +386,7 @@ def make(
_refresh_after_create: bool = False,
_from_manager=None,
_fill_optional: Union[List[str], bool] = False,
_use_faker_generator: Optional[bool] = False,
**attrs: Any,
):
"""Create and persist an instance of the model associated with Baker instance."""
Expand Down Expand Up @@ -660,6 +676,11 @@ def generate_value(self, field: Field, commit: bool = True) -> Any:
`attr_mapping` and `type_mapping` can be defined easily overwriting the
model.
"""
field_name = field.attname
if self._use_faker_generator and field_name in faker_generator_mapping:
generator = faker_generator_mapping.get(field_name)
return generator()

is_content_type_fk = isinstance(field, ForeignKey) and issubclass(
self._remote_field(field).model, contenttypes.models.ContentType
)
Expand Down
18 changes: 18 additions & 0 deletions model_bakery/faker_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from typing import Callable, Dict

from faker import Faker

FAKER = Faker()

faker_generator_mapping: Dict[str, Callable] = {
"username": FAKER.user_name,
"email": FAKER.email,
"first_name": FAKER.first_name,
"last_name": FAKER.last_name,
"name": FAKER.name,
"fullname": FAKER.name,
"full_name": FAKER.name,
"ip": FAKER.ipv4,
"ipv4": FAKER.ipv4,
"ipv6": FAKER.ipv6,
}
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
django>=3.2
Faker==15.1.3
39 changes: 39 additions & 0 deletions tests/test_baker.py
Original file line number Diff line number Diff line change
Expand Up @@ -1049,3 +1049,42 @@ def test_create(self):
)
c1, c2 = models.Classroom.objects.all()[:2]
assert list(c1.students.all()) == list(c2.students.all()) == [person]


class TestCreateProfileWithFakerGenerator(TestCase):
@pytest.mark.django_db
def test_create_profile_with_email_generated_by_faker(self):
profile = baker.make(
models.Profile,
_use_faker_generator=True,
)
assert profile.email

@pytest.mark.django_db
def test_create_profile_with_username_generated_by_faker(self):
user = baker.make(
models.User,
_use_faker_generator=True,
)
another_user = baker.make(models.User)
assert user.username and len(another_user.username) > len(user.username)

@pytest.mark.django_db
def test_create_person_with_name_generated_by_faker(self):
person = baker.make(
models.Person,
_use_faker_generator=True,
)
assert len(person.name.split(" ")) == 2

@pytest.mark.django_db
def test_create_person_with_name_generated_by_faker_different_than_default(self):
person = baker.make(
models.Person,
_use_faker_generator=True,
)
another_person = baker.make(models.Person)
assert (
len(another_person.name.split(" ")) == 1
and another_person.name != person.name
)