Skip to content
Draft
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
7 changes: 5 additions & 2 deletions src/marshmallow/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -2065,10 +2065,13 @@ class Constant(Field[_ContantT]):
_CHECK_ATTRIBUTE = False

def __init__(self, constant: _ContantT, **kwargs: Unpack[_BaseFieldKwargs]):
# Set load_default and dump_default before calling super().__init__
# so that allow_none is correctly computed when constant is None
# (fixes issue #2868)
kwargs.setdefault("load_default", constant)
kwargs.setdefault("dump_default", constant)
super().__init__(**kwargs)
self.constant = constant
self.load_default = constant
self.dump_default = constant

def _serialize(self, value, *args, **kwargs) -> _ContantT:
return self.constant
Expand Down
16 changes: 16 additions & 0 deletions tests/test_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,22 @@ class MySchema(Schema):
assert sch.load({})["foo"] == 42
assert sch.load({"foo": 24})["foo"] == 42

def test_constant_none_allows_null_input(self):
"""Test that Constant(None) correctly allows null input (issue #2868)."""

class MySchema(Schema):
sentinel = fields.Constant(None)

schema = MySchema()
# Should not raise ValidationError
result = schema.load({"sentinel": None})
assert result["sentinel"] is None
# Should also work with any other input
result = schema.load({"sentinel": "anything"})
assert result["sentinel"] is None
# Dump should also work
assert schema.dump({"sentinel": "anything"})["sentinel"] is None

def test_field_deserialization_with_user_validator_function(self):
field = fields.String(validate=predicate(lambda s: s.lower() == "valid"))
assert field.deserialize("Valid") == "Valid"
Expand Down