diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4d939a08d..1f312eea8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,7 +9,7 @@ Features: - Add ``validate.And`` (:issue:`1768`). Thanks :user:`rugleb` for the suggestion. - Let ``Field``s be accessed by name as ``Schema`` attributes (:pr:`1631`). -- Add a `NoDuplicates` validator in ``marshmallow.validate`` (:pr:`1793`). +- Add a `Unique` validator in ``marshmallow.validate`` (:pr:`1793`). Other changes: diff --git a/src/marshmallow/validate.py b/src/marshmallow/validate.py index 30ad958e7..89cbc6f3d 100644 --- a/src/marshmallow/validate.py +++ b/src/marshmallow/validate.py @@ -646,8 +646,8 @@ def __call__(self, value: typing.Sequence[_T]) -> typing.Sequence[_T]: return value -class NoDuplicates(Validator): - """Validator which succeeds if the ``value`` is an ``iterable`` and has no duplicate +class Unique(Validator): + """Validator which succeeds if the ``value`` is an ``iterable`` and has unique elements. In case of a list of objects, it can easy check an internal attribute by passing the ``attribute`` parameter. Validator which fails if ``value`` is not a member of ``iterable``. diff --git a/tests/test_validate.py b/tests/test_validate.py index 85018986b..7cf5ad556 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -914,7 +914,7 @@ def test_and(): assert errors == ["Not an even value.", "Must be less than or equal to 6."] -def test_noduplicates(): +def test_contains_unique(): class Mock: def __init__(self, name): self.name = name @@ -922,29 +922,30 @@ def __init__(self, name): mock_object_1 = Mock("a") mock_object_2 = Mock("b") - assert validate.NoDuplicates()("d") == "d" - assert validate.NoDuplicates()([]) == [] - assert validate.NoDuplicates()({}) == {} - assert validate.NoDuplicates()(["a", "b"]) == ["a", "b"] - assert validate.NoDuplicates()([1, 2]) == [1, 2] - assert validate.NoDuplicates(attribute="name")([mock_object_1, mock_object_2]) == [ + assert validate.Unique()("d") == "d" + assert validate.Unique()([]) == [] + assert validate.Unique()({}) == {} + assert validate.Unique()(["a", "b"]) == ["a", "b"] + assert validate.Unique()([1, 2]) == [1, 2] + assert validate.Unique(attribute="name")([mock_object_1, mock_object_2]) == [ mock_object_1, mock_object_2, ] with pytest.raises(ValidationError, match="Invalid input."): - validate.NoDuplicates()(3) + validate.Unique()(3) with pytest.raises(ValidationError, match="Invalid input."): - validate.NoDuplicates()(1.1) + validate.Unique()(1.1) with pytest.raises(ValidationError, match="Invalid input."): - validate.NoDuplicates()(True) + validate.Unique()(True) with pytest.raises(ValidationError, match="Invalid input."): - validate.NoDuplicates()(None) + validate.Unique()(None) with pytest.raises(ValidationError, match="Found a duplicate value: 1."): - validate.NoDuplicates()([1, 1, 2]) + validate.Unique()([1, 1, 2]) with pytest.raises(ValidationError, match="Found a duplicate value: a."): - validate.NoDuplicates()("aab") + validate.Unique()("aab") with pytest.raises(ValidationError, match="Found a duplicate value: a."): - validate.NoDuplicates()(["a", "a", "b"]) + validate.Unique()(["a", "a", "b"]) with pytest.raises(ValidationError, match="Found a duplicate object attribute"): - validate.NoDuplicates(attribute="name")([mock_object_1, mock_object_1]) + validate.Unique(attribute="name")([mock_object_1, mock_object_1]) +