Skip to content

Commit

Permalink
Run Entity.defaults before validations
Browse files Browse the repository at this point in the history
The `defaults` method was being run after setting `None` values to remaining
fields which would already triggered errors for fields marked `required`.
This commit runs `defaults` before so that field values can be set in `defaults`
method safely before firing ValidationError exceptions.

This method also contains fixes for test_support domains that need to explicitly
look for a domain.toml config file.
  • Loading branch information
subhashb committed Jun 6, 2024
1 parent 7d4731b commit 18e422b
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 8 deletions.
8 changes: 5 additions & 3 deletions src/protean/core/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,10 +306,14 @@ def __init__(self, *template, **kwargs): # noqa: C901
self, f"filter_{field_name}", partial(field_obj.filter, self)
)

self.defaults()

# Now load the remaining fields with a None value, which will fail
# for required fields
for field_name, field_obj in fields(self).items():
if field_name not in loaded_fields:
if field_name not in loaded_fields and (
not hasattr(self, field_name) or getattr(self, field_name) is None
):
if not isinstance(field_obj, Association):
try:
setattr(self, field_name, None)
Expand All @@ -326,8 +330,6 @@ def __init__(self, *template, **kwargs): # noqa: C901
if field_name not in loaded_fields and not hasattr(self, field_name):
setattr(self, field_name, None)

self.defaults()

self._initialized = True

# `_postcheck()` will return a `defaultdict(list)` if errors are to be raised
Expand Down
6 changes: 3 additions & 3 deletions tests/adapters/model/dict_model/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def test_that_model_class_is_created_automatically(self, test_domain):
assert issubclass(model_cls, MemoryModel)
assert model_cls.__name__ == "PersonModel"

def test_conversation_from_entity_to_model(self, test_domain):
def test_conversion_from_entity_to_model(self, test_domain):
model_cls = test_domain.repository_for(Person)._model

person = Person(first_name="John", last_name="Doe")
Expand All @@ -28,7 +28,7 @@ def test_conversation_from_entity_to_model(self, test_domain):
assert person_model_obj["first_name"] == "John"
assert person_model_obj["last_name"] == "Doe"

def test_conversation_from_model_to_entity(self, test_domain):
def test_conversion_from_model_to_entity(self, test_domain):
model_cls = test_domain.repository_for(Person)._model
person = Person(first_name="John", last_name="Doe")
person_model_obj = model_cls.from_entity(person)
Expand All @@ -47,7 +47,7 @@ def test_that_model_class_is_created_automatically(self, test_domain):
assert issubclass(model_cls, MemoryModel)
assert model_cls.__name__ == "UserModel"

def test_conversation_from_entity_to_model(self, test_domain):
def test_conversion_from_entity_to_model(self, test_domain):
model_cls = test_domain.repository_for(User)._model

user1 = User(email_address="[email protected]", password="d4e5r6")
Expand Down
11 changes: 11 additions & 0 deletions tests/entity/test_lifecycle_methods.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import pytest

from protean import BaseEntity
from protean.fields import String
from protean.exceptions import ValidationError

from .elements import Area, Building, BuildingStatus
Expand All @@ -16,6 +18,15 @@ def test_that_building_is_marked_as_done_if_below_4_floors(self):

assert building.status == BuildingStatus.WIP.value

def test_defaults_are_applied_before_validation(self):
class Foo(BaseEntity):
name = String(required=True)

def defaults(self):
self.name = "bar"

assert Foo().name == "bar"


class TestInvariantValidation:
def test_that_building_cannot_be_WIP_if_above_4_floors(self, test_domain):
Expand Down
2 changes: 1 addition & 1 deletion tests/support/test_domains/test18/domain18.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from protean import Domain


domain = Domain(__file__, "TEST18", load_toml=False)
domain = Domain(__file__, "TEST18")
2 changes: 1 addition & 1 deletion tests/support/test_domains/test19/domain19.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from protean import Domain


domain = Domain(__file__, "TEST19", load_toml=False)
domain = Domain(__file__, "TEST19")

0 comments on commit 18e422b

Please sign in to comment.