Skip to content

Commit

Permalink
[116] Support Callable default and Always Use default If Present (#…
Browse files Browse the repository at this point in the history
…117)

* [#116] Call field default when it is callable

- Also, always generate default value if `field.default` is supplied
  - Previously, this would not happen, since the `generator` determination
    was being done earlier in the same `if, elif` block

- Add unit tests for generating default value of callables of varying kinds

* [116] Add a unit test to cover kwarg over attr_mapping

* [116] Reduce complexity of custom field class

* [116] Update CHANGELOG

Co-authored-by: Bernardo Fontes <[email protected]>
  • Loading branch information
timjk-gp and berinhard authored Oct 13, 2020
1 parent de521c7 commit a7e723e
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

### Changed
- Fixed _model parameter annotations [PR #115](https://github.com/model-bakers/model_bakery/pull/115)
- Fixes bug when field has callable `default` [PR #117](https://github.com/model-bakers/model_bakery/pull/117)
- [dev] Drop Python 3.5 support as it is retired (https://www.python.org/downloads/release/python-3510/)

### Removed
Expand Down
8 changes: 5 additions & 3 deletions model_bakery/baker.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,11 @@ def generate_value(self, field: Field, commit: bool = True) -> Any:
self._remote_field(field).model, contenttypes.models.ContentType
)

if field.name in self.attr_mapping:
if field.has_default():
if callable(field.default):
return field.default()
return field.default
elif field.name in self.attr_mapping:
generator = self.attr_mapping[field.name]
elif getattr(field, "choices"):
generator = random_gen.gen_from_choices(field.choices)
Expand All @@ -525,8 +529,6 @@ def generate_value(self, field: Field, commit: bool = True) -> Any:
generator = generators.get(field.__class__)
elif field.__class__ in self.type_mapping:
generator = self.type_mapping[field.__class__]
elif field.has_default():
return field.default
else:
raise TypeError("%s is not supported by baker." % field.__class__)

Expand Down
8 changes: 8 additions & 0 deletions tests/generic/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.core.files.storage import FileSystemStorage
from django.utils.timezone import now

from model_bakery.gis import BAKER_GIS
from model_bakery.timezone import smart_datetime as datetime
Expand Down Expand Up @@ -265,6 +266,10 @@ class DummyBlankFieldsModel(models.Model):
blank_text_field = models.TextField(max_length=300, blank=True)


class ExtendedDefaultField(models.IntegerField):
pass


class DummyDefaultFieldsModel(models.Model):
default_id = models.AutoField(primary_key=True)
default_char_field = models.CharField(max_length=50, default="default")
Expand All @@ -279,6 +284,9 @@ class DummyDefaultFieldsModel(models.Model):
)
default_email_field = models.EmailField(default="[email protected]")
default_slug_field = models.SlugField(default="a-slug")
default_unknown_class_field = ExtendedDefaultField(default=42)
default_callable_int_field = models.IntegerField(default=lambda: 12)
default_callable_datetime_field = models.DateTimeField(default=now)


class DummyFileFieldModel(models.Model):
Expand Down
12 changes: 12 additions & 0 deletions tests/test_baker.py
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,15 @@ def test_fill_optional_with_integer(self):
with pytest.raises(TypeError):
baker.make(models.DummyBlankFieldsModel, _fill_optional=1)

def test_fill_optional_with_default(self):
dummy = baker.make(models.DummyDefaultFieldsModel, _fill_optional=True)
assert dummy.default_callable_int_field == 12
assert isinstance(dummy.default_callable_datetime_field, datetime.datetime)

def test_fill_optional_with_default_unknown_class(self):
dummy = baker.make(models.DummyDefaultFieldsModel, _fill_optional=True)
assert dummy.default_unknown_class_field == 42


@pytest.mark.django_db
class TestFillAutoFieldsTestCase:
Expand Down Expand Up @@ -572,6 +581,9 @@ def test_skip_fields_with_default(self):
assert dummy.default_decimal_field == Decimal("0")
assert dummy.default_email_field == "[email protected]"
assert dummy.default_slug_field == "a-slug"
assert dummy.default_unknown_class_field == 42
assert dummy.default_callable_int_field == 12
assert isinstance(dummy.default_callable_datetime_field, datetime.datetime)


@pytest.mark.django_db
Expand Down
10 changes: 8 additions & 2 deletions tests/test_extending_bakery.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class SadPeopleBaker(baker.Baker):
attr_mapping = {
"enjoy_jards_macale": gen_opposite,
"like_metal_music": gen_opposite,
"name": gen_opposite, # Use a field without `default`
}


Expand All @@ -53,8 +54,13 @@ def test_string_to_generator_required(self):
like_metal_music_field = Person._meta.get_field("like_metal_music")
sad_people_factory = SadPeopleBaker(Person)
person = sad_people_factory.make()
assert person.enjoy_jards_macale is not enjoy_jards_macale_field.default
assert person.like_metal_music is not like_metal_music_field.default
assert person.enjoy_jards_macale is enjoy_jards_macale_field.default
assert person.like_metal_music is like_metal_music_field.default

def test_kwarg_used_over_attr_mapping_generator(self):
sad_people_factory = SadPeopleBaker(Person)
person = sad_people_factory.make(name="test")
assert person.name == "test"

@pytest.mark.parametrize("value", [18, 18.5, [], {}, True])
def test_fail_pass_non_string_to_generator_required(self, value):
Expand Down

0 comments on commit a7e723e

Please sign in to comment.