2014-09-29 14:23:02 +04:00
"""
We perform uniqueness checks explicitly on the serializer class , rather
the using Django ' s `.full_clean()`.
2014-10-02 23:41:18 +04:00
This gives us better separation of concerns , allows us to use single - step
2014-09-29 14:23:02 +04:00
object creation , and makes it possible to switch between using the implicit
2014-12-05 02:29:28 +03:00
` ModelSerializer ` class and an equivalent explicit ` Serializer ` class .
2014-09-29 14:23:02 +04:00
"""
2016-06-23 17:09:23 +03:00
from django . db import DataError
2024-06-23 16:54:08 +03:00
from django . db . models import Exists
Replace all usage ugettext functions with the non-u versions (#6634)
On Python 3, the ugettext functions are a simple aliases of their non-u
counterparts (the 'u' represents Python 2 unicode type). Starting with
Django 3.0, the u versions will be deprecated.
https://docs.djangoproject.com/en/dev/releases/3.0/#id2
> django.utils.translation.ugettext(), ugettext_lazy(), ugettext_noop(),
> ungettext(), and ungettext_lazy() are deprecated in favor of the
> functions that they’re aliases for:
> django.utils.translation.gettext(), gettext_lazy(), gettext_noop(),
> ngettext(), and ngettext_lazy().
2019-05-01 08:49:54 +03:00
from django . utils . translation import gettext_lazy as _
2015-06-18 16:38:29 +03:00
2014-10-22 16:30:28 +04:00
from rest_framework . exceptions import ValidationError
2014-09-29 14:23:02 +04:00
from rest_framework . utils . representation import smart_repr
2014-09-29 12:24:03 +04:00
2016-06-23 17:09:23 +03:00
# Robust filter and exist implementations. Ensures that queryset.exists() for
# an invalid value returns `False`, rather than raising an error.
2017-04-07 17:28:35 +03:00
# Refs https://github.com/encode/django-rest-framework/issues/3381
2016-06-23 17:09:23 +03:00
def qs_exists ( queryset ) :
try :
return queryset . exists ( )
except ( TypeError , ValueError , DataError ) :
return False
2024-06-23 16:54:08 +03:00
def qs_exists_with_condition ( queryset , condition , against ) :
if condition is None :
return qs_exists ( queryset )
try :
# use the same query as UniqueConstraint.validate https://github.com/django/django/blob/7ba2a0db20c37a5b1500434ca4ed48022311c171/django/db/models/constraints.py#L672
return ( condition & Exists ( queryset . filter ( condition ) ) ) . check ( against )
except ( TypeError , ValueError , DataError ) :
return False
2016-06-23 17:09:23 +03:00
def qs_filter ( queryset , * * kwargs ) :
try :
return queryset . filter ( * * kwargs )
except ( TypeError , ValueError , DataError ) :
return queryset . none ( )
2019-04-30 18:53:44 +03:00
class UniqueValidator :
2014-10-09 13:11:44 +04:00
"""
Validator that corresponds to ` unique = True ` on a model field .
2014-10-22 16:30:28 +04:00
Should be applied to an individual field on the serializer .
2014-10-09 13:11:44 +04:00
"""
2014-09-29 14:23:02 +04:00
message = _ ( ' This field must be unique. ' )
2019-12-03 14:16:27 +03:00
requires_context = True
2014-09-29 12:24:03 +04:00
2016-10-04 15:44:50 +03:00
def __init__ ( self , queryset , message = None , lookup = ' exact ' ) :
2014-09-29 12:24:03 +04:00
self . queryset = queryset
2014-10-31 16:47:36 +03:00
self . message = message or self . message
2016-10-04 15:44:50 +03:00
self . lookup = lookup
2014-09-29 12:24:03 +04:00
2019-12-03 14:16:27 +03:00
def filter_queryset ( self , value , queryset , field_name ) :
2014-11-10 15:32:03 +03:00
"""
Filter the queryset to all instances matching the given attribute .
"""
2019-12-03 14:16:27 +03:00
filter_kwargs = { ' %s __ %s ' % ( field_name , self . lookup ) : value }
2016-06-23 17:09:23 +03:00
return qs_filter ( queryset , * * filter_kwargs )
2014-11-10 15:32:03 +03:00
2019-12-03 14:16:27 +03:00
def exclude_current_instance ( self , queryset , instance ) :
2014-11-10 15:32:03 +03:00
"""
If an instance is being updated , then do not include
that instance itself as a uniqueness conflict .
"""
2019-12-03 14:16:27 +03:00
if instance is not None :
return queryset . exclude ( pk = instance . pk )
2014-11-10 15:32:03 +03:00
return queryset
2019-12-03 14:16:27 +03:00
def __call__ ( self , value , serializer_field ) :
# Determine the underlying model field name. This may not be the
# same as the serializer field name if `source=<>` is set.
field_name = serializer_field . source_attrs [ - 1 ]
# Determine the existing instance, if this is an update operation.
instance = getattr ( serializer_field . parent , ' instance ' , None )
2014-11-10 15:32:03 +03:00
queryset = self . queryset
2019-12-03 14:16:27 +03:00
queryset = self . filter_queryset ( value , queryset , field_name )
queryset = self . exclude_current_instance ( queryset , instance )
2016-06-23 17:09:23 +03:00
if qs_exists ( queryset ) :
2016-10-11 12:25:21 +03:00
raise ValidationError ( self . message , code = ' unique ' )
2014-09-29 14:23:02 +04:00
def __repr__ ( self ) :
2019-04-30 18:53:44 +03:00
return ' < %s (queryset= %s )> ' % (
2014-09-29 14:23:02 +04:00
self . __class__ . __name__ ,
smart_repr ( self . queryset )
2019-04-30 18:53:44 +03:00
)
2014-09-29 12:24:03 +04:00
2023-04-09 11:53:47 +03:00
def __eq__ ( self , other ) :
if not isinstance ( other , self . __class__ ) :
return NotImplemented
return ( self . message == other . message
and self . requires_context == other . requires_context
and self . queryset == other . queryset
and self . lookup == other . lookup
)
2014-09-29 12:24:03 +04:00
2019-04-30 18:53:44 +03:00
class UniqueTogetherValidator :
2014-10-09 13:11:44 +04:00
"""
Validator that corresponds to ` unique_together = ( . . . ) ` on a model class .
2014-10-22 16:30:28 +04:00
Should be applied to the serializer class , not to an individual field .
2014-10-09 13:11:44 +04:00
"""
2014-09-29 14:23:02 +04:00
message = _ ( ' The fields {field_names} must make a unique set. ' )
2014-11-10 15:21:27 +03:00
missing_message = _ ( ' This field is required. ' )
2019-12-03 14:16:27 +03:00
requires_context = True
2014-09-29 12:24:03 +04:00
2024-06-23 16:54:08 +03:00
def __init__ ( self , queryset , fields , message = None , condition_fields = None , condition = None ) :
2014-09-29 12:24:03 +04:00
self . queryset = queryset
self . fields = fields
2014-10-31 16:47:36 +03:00
self . message = message or self . message
2024-06-23 16:54:08 +03:00
self . condition_fields = [ ] if condition_fields is None else condition_fields
self . condition = condition
2014-09-29 12:24:03 +04:00
2019-12-11 11:44:08 +03:00
def enforce_required_fields ( self , attrs , serializer ) :
2014-11-10 15:21:27 +03:00
"""
The ` UniqueTogetherValidator ` always forces an implied ' required '
state on the fields it applies to .
"""
2019-12-11 11:44:08 +03:00
if serializer . instance is not None :
2014-11-19 16:55:10 +03:00
return
2016-10-11 12:25:21 +03:00
missing_items = {
2015-10-17 12:00:11 +03:00
field_name : self . missing_message
2024-06-23 16:54:08 +03:00
for field_name in ( * self . fields , * self . condition_fields )
2019-12-12 16:02:30 +03:00
if serializer . fields [ field_name ] . source not in attrs
2015-10-17 12:00:11 +03:00
}
2016-10-11 12:25:21 +03:00
if missing_items :
raise ValidationError ( missing_items , code = ' required ' )
2014-11-10 15:21:27 +03:00
2019-12-11 11:44:08 +03:00
def filter_queryset ( self , attrs , queryset , serializer ) :
2014-11-10 15:21:27 +03:00
"""
Filter the queryset to all instances matching the given attributes .
"""
2019-12-12 16:02:30 +03:00
# field names => field sources
sources = [
serializer . fields [ field_name ] . source
for field_name in self . fields
]
2014-11-19 16:55:10 +03:00
# If this is an update, then any unprovided field should
# have it's value set based on the existing instance attribute.
2019-12-11 11:44:08 +03:00
if serializer . instance is not None :
2019-12-12 16:02:30 +03:00
for source in sources :
if source not in attrs :
attrs [ source ] = getattr ( serializer . instance , source )
2014-11-19 16:55:10 +03:00
# Determine the filter keyword arguments and filter the queryset.
2015-10-17 12:00:11 +03:00
filter_kwargs = {
2019-12-12 16:02:30 +03:00
source : attrs [ source ]
for source in sources
2015-10-17 12:00:11 +03:00
}
2016-06-23 17:09:23 +03:00
return qs_filter ( queryset , * * filter_kwargs )
2014-11-10 15:21:27 +03:00
2019-12-03 14:16:27 +03:00
def exclude_current_instance ( self , attrs , queryset , instance ) :
2014-11-10 15:21:27 +03:00
"""
If an instance is being updated , then do not include
that instance itself as a uniqueness conflict .
"""
2019-12-03 14:16:27 +03:00
if instance is not None :
return queryset . exclude ( pk = instance . pk )
2014-11-10 15:21:27 +03:00
return queryset
2019-12-03 14:16:27 +03:00
def __call__ ( self , attrs , serializer ) :
2019-12-11 11:44:08 +03:00
self . enforce_required_fields ( attrs , serializer )
2014-11-10 15:21:27 +03:00
queryset = self . queryset
2019-12-11 11:44:08 +03:00
queryset = self . filter_queryset ( attrs , queryset , serializer )
queryset = self . exclude_current_instance ( attrs , queryset , serializer . instance )
2015-02-18 21:00:12 +03:00
# Ignore validation if any field is None
2024-01-26 13:36:18 +03:00
if serializer . instance is None :
checked_values = [
value for field , value in attrs . items ( ) if field in self . fields
]
else :
# Ignore validation if all field values are unchanged
checked_values = [
value
for field , value in attrs . items ( )
if field in self . fields and value != getattr ( serializer . instance , field )
]
2024-06-23 16:54:08 +03:00
condition_kwargs = {
source : attrs [ source ]
for source in self . condition_fields
}
if checked_values and None not in checked_values and qs_exists_with_condition ( queryset , self . condition , condition_kwargs ) :
2014-09-29 14:23:02 +04:00
field_names = ' , ' . join ( self . fields )
2016-10-11 12:25:21 +03:00
message = self . message . format ( field_names = field_names )
raise ValidationError ( message , code = ' unique ' )
2014-09-29 14:23:02 +04:00
def __repr__ ( self ) :
2024-06-23 16:54:08 +03:00
return ' < %s ( %s )> ' % (
2014-09-29 14:23:02 +04:00
self . __class__ . __name__ ,
2024-06-23 16:54:08 +03:00
' , ' . join (
f ' { attr } = { smart_repr ( getattr ( self , attr ) ) } '
for attr in ( ' queryset ' , ' fields ' , ' condition ' )
if getattr ( self , attr ) is not None )
2019-04-30 18:53:44 +03:00
)
2014-10-22 16:30:28 +04:00
2023-04-09 11:53:47 +03:00
def __eq__ ( self , other ) :
if not isinstance ( other , self . __class__ ) :
return NotImplemented
return ( self . message == other . message
and self . requires_context == other . requires_context
and self . missing_message == other . missing_message
and self . queryset == other . queryset
and self . fields == other . fields
)
2014-10-22 16:30:28 +04:00
2020-01-06 17:12:21 +03:00
class ProhibitSurrogateCharactersValidator :
message = _ ( ' Surrogate characters are not allowed: U+ {code_point:X} . ' )
code = ' surrogate_characters_not_allowed '
def __call__ ( self , value ) :
for surrogate_character in ( ch for ch in str ( value )
if 0xD800 < = ord ( ch ) < = 0xDFFF ) :
message = self . message . format ( code_point = ord ( surrogate_character ) )
raise ValidationError ( message , code = self . code )
2023-04-09 11:53:47 +03:00
def __eq__ ( self , other ) :
if not isinstance ( other , self . __class__ ) :
return NotImplemented
return ( self . message == other . message
and self . code == other . code
)
2020-01-06 17:12:21 +03:00
2019-04-30 18:53:44 +03:00
class BaseUniqueForValidator :
2014-10-22 16:30:28 +04:00
message = None
2014-11-10 15:21:27 +03:00
missing_message = _ ( ' This field is required. ' )
2019-12-03 14:16:27 +03:00
requires_context = True
2014-10-22 16:30:28 +04:00
2014-10-31 16:47:36 +03:00
def __init__ ( self , queryset , field , date_field , message = None ) :
2014-10-22 16:30:28 +04:00
self . queryset = queryset
self . field = field
self . date_field = date_field
2014-10-31 16:47:36 +03:00
self . message = message or self . message
2014-10-22 16:30:28 +04:00
2014-11-10 15:21:27 +03:00
def enforce_required_fields ( self , attrs ) :
"""
The ` UniqueFor < Range > Validator ` classes always force an implied
' required ' state on the fields they are applied to .
"""
2016-10-11 12:25:21 +03:00
missing_items = {
2015-10-17 12:00:11 +03:00
field_name : self . missing_message
2014-11-10 15:21:27 +03:00
for field_name in [ self . field , self . date_field ]
if field_name not in attrs
2015-10-17 12:00:11 +03:00
}
2016-10-11 12:25:21 +03:00
if missing_items :
raise ValidationError ( missing_items , code = ' required ' )
2014-10-22 16:30:28 +04:00
2019-12-03 14:16:27 +03:00
def filter_queryset ( self , attrs , queryset , field_name , date_field_name ) :
2014-11-10 15:21:27 +03:00
raise NotImplementedError ( ' `filter_queryset` must be implemented. ' )
2014-10-22 16:30:28 +04:00
2019-12-03 14:16:27 +03:00
def exclude_current_instance ( self , attrs , queryset , instance ) :
2014-11-10 15:21:27 +03:00
"""
If an instance is being updated , then do not include
that instance itself as a uniqueness conflict .
"""
2019-12-03 14:16:27 +03:00
if instance is not None :
return queryset . exclude ( pk = instance . pk )
2014-11-10 15:21:27 +03:00
return queryset
2019-12-03 14:16:27 +03:00
def __call__ ( self , attrs , serializer ) :
# Determine the underlying model field names. These may not be the
# same as the serializer field names if `source=<>` is set.
field_name = serializer . fields [ self . field ] . source_attrs [ - 1 ]
date_field_name = serializer . fields [ self . date_field ] . source_attrs [ - 1 ]
2014-11-10 15:21:27 +03:00
self . enforce_required_fields ( attrs )
queryset = self . queryset
2019-12-03 14:16:27 +03:00
queryset = self . filter_queryset ( attrs , queryset , field_name , date_field_name )
2019-12-11 11:44:08 +03:00
queryset = self . exclude_current_instance ( attrs , queryset , serializer . instance )
2016-06-23 17:09:23 +03:00
if qs_exists ( queryset ) :
2014-10-22 16:30:28 +04:00
message = self . message . format ( date_field = self . date_field )
2016-10-11 12:25:21 +03:00
raise ValidationError ( {
self . field : message
} , code = ' unique ' )
2014-10-22 16:30:28 +04:00
2023-04-09 11:53:47 +03:00
def __eq__ ( self , other ) :
if not isinstance ( other , self . __class__ ) :
return NotImplemented
return ( self . message == other . message
and self . missing_message == other . missing_message
and self . requires_context == other . requires_context
and self . queryset == other . queryset
and self . field == other . field
and self . date_field == other . date_field
)
2014-10-22 16:30:28 +04:00
def __repr__ ( self ) :
2019-04-30 18:53:44 +03:00
return ' < %s (queryset= %s , field= %s , date_field= %s )> ' % (
2014-10-22 16:30:28 +04:00
self . __class__ . __name__ ,
smart_repr ( self . queryset ) ,
smart_repr ( self . field ) ,
smart_repr ( self . date_field )
2019-04-30 18:53:44 +03:00
)
2014-10-22 16:30:28 +04:00
class UniqueForDateValidator ( BaseUniqueForValidator ) :
message = _ ( ' This field must be unique for the " {date_field} " date. ' )
2019-12-03 14:16:27 +03:00
def filter_queryset ( self , attrs , queryset , field_name , date_field_name ) :
2014-10-22 16:30:28 +04:00
value = attrs [ self . field ]
date = attrs [ self . date_field ]
filter_kwargs = { }
2019-12-03 14:16:27 +03:00
filter_kwargs [ field_name ] = value
filter_kwargs [ ' %s __day ' % date_field_name ] = date . day
filter_kwargs [ ' %s __month ' % date_field_name ] = date . month
filter_kwargs [ ' %s __year ' % date_field_name ] = date . year
2016-06-23 17:09:23 +03:00
return qs_filter ( queryset , * * filter_kwargs )
2014-10-22 16:30:28 +04:00
class UniqueForMonthValidator ( BaseUniqueForValidator ) :
message = _ ( ' This field must be unique for the " {date_field} " month. ' )
2019-12-03 14:16:27 +03:00
def filter_queryset ( self , attrs , queryset , field_name , date_field_name ) :
2014-10-22 16:30:28 +04:00
value = attrs [ self . field ]
date = attrs [ self . date_field ]
filter_kwargs = { }
2019-12-03 14:16:27 +03:00
filter_kwargs [ field_name ] = value
filter_kwargs [ ' %s __month ' % date_field_name ] = date . month
2016-06-23 17:09:23 +03:00
return qs_filter ( queryset , * * filter_kwargs )
2014-10-22 16:30:28 +04:00
class UniqueForYearValidator ( BaseUniqueForValidator ) :
message = _ ( ' This field must be unique for the " {date_field} " year. ' )
2019-12-03 14:16:27 +03:00
def filter_queryset ( self , attrs , queryset , field_name , date_field_name ) :
2014-10-22 16:30:28 +04:00
value = attrs [ self . field ]
date = attrs [ self . date_field ]
filter_kwargs = { }
2019-12-03 14:16:27 +03:00
filter_kwargs [ field_name ] = value
filter_kwargs [ ' %s __year ' % date_field_name ] = date . year
2016-06-23 17:09:23 +03:00
return qs_filter ( queryset , * * filter_kwargs )