Skip to content

Commit 9576e83

Browse files
committed
Move null environment check to object-level validate()
Field-level validate_environment was blocking the context fallback in validate() from ever running. Now validate_environment just guards the .id access, and validate() resolves environment from context before rejecting null.
1 parent 5e4bac7 commit 9576e83

File tree

2 files changed

+32
-7
lines changed

2 files changed

+32
-7
lines changed

api/features/serializers.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -597,16 +597,18 @@ def validate_feature(self, feature): # type: ignore[no-untyped-def]
597597
return feature
598598

599599
def validate_environment(self, environment): # type: ignore[no-untyped-def]
600-
if environment is None:
601-
raise serializers.ValidationError("This field may not be null.")
602-
if self.instance and self.instance.environment_id != environment.id: # type: ignore[union-attr]
600+
if environment is not None and self.instance and self.instance.environment_id != environment.id: # type: ignore[union-attr]
603601
raise serializers.ValidationError(
604602
"Cannot change the environment of a feature state"
605603
)
606604
return environment
607605

608606
def validate(self, attrs): # type: ignore[no-untyped-def]
609-
environment = attrs.get("environment") or self.context["environment"]
607+
environment = attrs.get("environment") or self.context.get("environment")
608+
if environment is None:
609+
raise serializers.ValidationError(
610+
{"environment": ["This field may not be null."]}
611+
)
610612
identity = attrs.get("identity")
611613
feature_segment = attrs.get("feature_segment")
612614
identifier = attrs.pop("identifier", None)

api/tests/unit/features/test_unit_features_serializers.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@
1414
from features.serializers import FeatureStateSerializerBasic
1515

1616

17-
def test_feature_state_serializer_basic__null_environment__returns_validation_error( # type: ignore[no-untyped-def]
17+
def test_feature_state_serializer_basic__null_environment_no_context__returns_validation_error( # type: ignore[no-untyped-def]
1818
feature, environment
1919
):
20-
# Given
20+
# Given - null environment in payload and no environment in context
2121
feature_state = FeatureState.objects.get(feature=feature, environment=environment)
2222
data = {
2323
"id": feature_state.id,
@@ -27,7 +27,7 @@ def test_feature_state_serializer_basic__null_environment__returns_validation_er
2727
serializer = FeatureStateSerializerBasic(
2828
instance=feature_state,
2929
data=data,
30-
context={"environment": environment},
30+
context={},
3131
)
3232

3333
# When
@@ -38,6 +38,29 @@ def test_feature_state_serializer_basic__null_environment__returns_validation_er
3838
assert "environment" in serializer.errors
3939

4040

41+
def test_feature_state_serializer_basic__null_environment_with_context__falls_back_to_context( # type: ignore[no-untyped-def]
42+
feature, environment
43+
):
44+
# Given - null environment in payload but valid environment in context
45+
feature_state = FeatureState.objects.get(feature=feature, environment=environment)
46+
data = {
47+
"id": feature_state.id,
48+
"feature": feature.id,
49+
"environment": None,
50+
}
51+
serializer = FeatureStateSerializerBasic(
52+
instance=feature_state,
53+
data=data,
54+
context={"environment": environment},
55+
)
56+
57+
# When
58+
is_valid = serializer.is_valid()
59+
60+
# Then - should fall back to context environment
61+
assert is_valid
62+
63+
4164
@pytest.mark.parametrize(
4265
"percentage_value, expected_is_valid", ((90, True), (100, True), (110, False))
4366
)

0 commit comments

Comments
 (0)