Skip to content

Commit

Permalink
[#31] Do not validate defaults for Django fields with choices
Browse files Browse the repository at this point in the history
The reason here is that the source of our config values cannot
express complex types such as enum members, and that defaults
for Django fields with choices frequently include such types
(or callables returning such types). We are mainly about the
types of the values provided from outside, not of Django field
defaults (beyond null and blank), so we can leave the validation
of the default to Django.
  • Loading branch information
swrichards committed Dec 17, 2024
1 parent 815b425 commit d210fc0
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 0 deletions.
11 changes: 11 additions & 0 deletions django_setup_configuration/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,17 @@ def __init__(
else:
field_info_creation_kwargs["default"] = inferred_default

# Defaults for fields with choices often do not map neatly onto the consructed
# type used for serialization (e.g. a string) because they may not be a literal
# choices but e.g. an enum/Choices member. Inferring the types of the default
# can be non-trivial (especially if a default factory is involved), and because
# we care about types that can be expressed as simple YAML/JSON scalars, it also
# would not make much sense to add complex types to the annotation.
validate_defaults = False if self.django_field.choices else True
field_info_creation_kwargs["validate_default"] = field_info_creation_kwargs[
"validate_return"
] = validate_defaults

return super().__init__(**field_info_creation_kwargs)

@staticmethod
Expand Down
6 changes: 6 additions & 0 deletions testapp/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ class TestModel(models.Model):
str_with_choices_and_default = models.CharField(
max_length=3, choices=StrChoices.choices, default=StrChoices.bar
)
str_with_choices_and_incorrectly_typed_default = models.CharField(
max_length=3, choices=StrChoices.choices, default=1974
)
str_with_choices_and_incorrectly_typed_default_factory = models.CharField(
max_length=3, choices=StrChoices.choices, default=lambda: 1985
)
str_with_choices_and_blank = models.CharField(
max_length=3, choices=StrChoices.choices, blank=True
)
Expand Down
15 changes: 15 additions & 0 deletions tests/test_django_model_ref_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,3 +372,18 @@ class Config(ConfigurationModel):
assert field.annotation == Literal[1, 8] | Literal[42]
assert field.default == 42
assert field.is_required() is False


def test_choices_with_incorrectly_typed_default_is_not_validated():

class Config(ConfigurationModel):
str_with_choices_and_incorrectly_typed_default = DjangoModelRef(
TestModel, "str_with_choices_and_incorrectly_typed_default"
)
str_with_choices_and_incorrectly_typed_default_factory = DjangoModelRef(
TestModel, "str_with_choices_and_incorrectly_typed_default_factory"
)

config = Config()
assert config.str_with_choices_and_incorrectly_typed_default == 1974
assert config.str_with_choices_and_incorrectly_typed_default_factory == 1985

0 comments on commit d210fc0

Please sign in to comment.