Marshmallow: Feature request: Add Unique validator

Created on 16 Mar 2020  路  3Comments  路  Source: marshmallow-code/marshmallow

Validates that all values present in a list input is unique, ie. didn't occur more than once.

enhancement feedback welcome

Most helpful comment

Not entirely opposed to adding this, but it's a bit tricky to handle both hashable and non-hashable types. Also, it's pretty easy to define this in userland:

def validate_uniqueness(value: Sequence):
    if len(set(value)) < len(value):
        raise ValidationError("Values must be unique.")

class MySchema(Schema):
    my_field = fields.List(fields.Str(), validate=validate_uniqueness)

I'd recommend doing that for now.

All 3 comments

could be subclass of List or another solution is to use unique=True keyword argument to List field class
and implement code bellow directly into List field

from collections import Counter
from marshmallow.validate import ValidationError

class UniqueList(fields.List):

    default_error_messages = {'unique': 'Values not unique.'}

    def _deserialize(self, value, attr, data, **kwargs) -> typing.List[typing.Any]:
        try:
            result = super()._deserialize(value, attr, data, **kwargs)
        except ValidationError as error:
            raise error
        unique_result = set(result)
        if len(result) > len(unique_result):
            counts = Counter(result)
            unique = list(dict(list(filter(lambda i: i[1] == 1, Counter(result).items()))).keys())
            msg = self.error_messages['unique']
            raise ValidationError(msg, valid_data=unique)
        return result

Not entirely opposed to adding this, but it's a bit tricky to handle both hashable and non-hashable types. Also, it's pretty easy to define this in userland:

def validate_uniqueness(value: Sequence):
    if len(set(value)) < len(value):
        raise ValidationError("Values must be unique.")

class MySchema(Schema):
    my_field = fields.List(fields.Str(), validate=validate_uniqueness)

I'd recommend doing that for now.

Closing for now, as we don't have plans to add this to core in the near future and the use case can easily be met in userland (see my comment above)

Was this page helpful?
0 / 5 - 0 ratings