Validator that corresponds to `unique=True` on a model field. Should be applied to an individual field on the serializer.
| 44 | |
| 45 | |
| 46 | class UniqueValidator: |
| 47 | """ |
| 48 | Validator that corresponds to `unique=True` on a model field. |
| 49 | |
| 50 | Should be applied to an individual field on the serializer. |
| 51 | """ |
| 52 | message = _('This field must be unique.') |
| 53 | requires_context = True |
| 54 | |
| 55 | def __init__(self, queryset, message=None, lookup='exact'): |
| 56 | self.queryset = queryset |
| 57 | self.message = message or self.message |
| 58 | self.lookup = lookup |
| 59 | |
| 60 | def filter_queryset(self, value, queryset, field_name): |
| 61 | """ |
| 62 | Filter the queryset to all instances matching the given attribute. |
| 63 | """ |
| 64 | filter_kwargs = {'%s__%s' % (field_name, self.lookup): value} |
| 65 | return qs_filter(queryset, **filter_kwargs) |
| 66 | |
| 67 | def exclude_current_instance(self, queryset, instance): |
| 68 | """ |
| 69 | If an instance is being updated, then do not include |
| 70 | that instance itself as a uniqueness conflict. |
| 71 | """ |
| 72 | if instance is not None: |
| 73 | return queryset.exclude(pk=instance.pk) |
| 74 | return queryset |
| 75 | |
| 76 | def __call__(self, value, serializer_field): |
| 77 | # Determine the underlying model field name. This may not be the |
| 78 | # same as the serializer field name if `source=<>` is set. |
| 79 | field_name = serializer_field.source_attrs[-1] |
| 80 | # Determine the existing instance, if this is an update operation. |
| 81 | instance = getattr(serializer_field.parent, 'instance', None) |
| 82 | |
| 83 | queryset = self.queryset |
| 84 | queryset = self.filter_queryset(value, queryset, field_name) |
| 85 | queryset = self.exclude_current_instance(queryset, instance) |
| 86 | if qs_exists(queryset): |
| 87 | raise ValidationError(self.message, code='unique') |
| 88 | |
| 89 | def __repr__(self): |
| 90 | return '<%s(queryset=%s)>' % ( |
| 91 | self.__class__.__name__, |
| 92 | smart_repr(self.queryset) |
| 93 | ) |
| 94 | |
| 95 | def __eq__(self, other): |
| 96 | if not isinstance(other, self.__class__): |
| 97 | return NotImplemented |
| 98 | return (self.message == other.message |
| 99 | and self.requires_context == other.requires_context |
| 100 | and self.queryset == other.queryset |
| 101 | and self.lookup == other.lookup |
| 102 | ) |
| 103 |
no test coverage detected