8
8
"""
9
9
from __future__ import unicode_literals
10
10
11
+ from django .db import DataError
11
12
from django .utils .translation import ugettext_lazy as _
12
13
13
14
from rest_framework .compat import unicode_to_repr
14
15
from rest_framework .exceptions import ValidationError
15
16
from rest_framework .utils .representation import smart_repr
16
17
17
18
19
+ # Robust filter and exist implementations. Ensures that queryset.exists() for
20
+ # an invalid value returns `False`, rather than raising an error.
21
+ # Refs https://github.com/tomchristie/django-rest-framework/issues/3381
22
+
23
+ def qs_exists (queryset ):
24
+ try :
25
+ return queryset .exists ()
26
+ except (TypeError , ValueError , DataError ):
27
+ return False
28
+
29
+
30
+ def qs_filter (queryset , ** kwargs ):
31
+ try :
32
+ return queryset .filter (** kwargs )
33
+ except (TypeError , ValueError , DataError ):
34
+ return queryset .none ()
35
+
36
+
18
37
class UniqueValidator (object ):
19
38
"""
20
39
Validator that corresponds to `unique=True` on a model field.
@@ -44,7 +63,7 @@ def filter_queryset(self, value, queryset):
44
63
Filter the queryset to all instances matching the given attribute.
45
64
"""
46
65
filter_kwargs = {self .field_name : value }
47
- return queryset . filter ( ** filter_kwargs )
66
+ return qs_filter ( queryset , ** filter_kwargs )
48
67
49
68
def exclude_current_instance (self , queryset ):
50
69
"""
@@ -59,7 +78,7 @@ def __call__(self, value):
59
78
queryset = self .queryset
60
79
queryset = self .filter_queryset (value , queryset )
61
80
queryset = self .exclude_current_instance (queryset )
62
- if queryset . exists ( ):
81
+ if qs_exists ( queryset ):
63
82
raise ValidationError (self .message )
64
83
65
84
def __repr__ (self ):
@@ -124,7 +143,7 @@ def filter_queryset(self, attrs, queryset):
124
143
field_name : attrs [field_name ]
125
144
for field_name in self .fields
126
145
}
127
- return queryset . filter ( ** filter_kwargs )
146
+ return qs_filter ( queryset , ** filter_kwargs )
128
147
129
148
def exclude_current_instance (self , attrs , queryset ):
130
149
"""
@@ -145,7 +164,7 @@ def __call__(self, attrs):
145
164
checked_values = [
146
165
value for field , value in attrs .items () if field in self .fields
147
166
]
148
- if None not in checked_values and queryset . exists ( ):
167
+ if None not in checked_values and qs_exists ( queryset ):
149
168
field_names = ', ' .join (self .fields )
150
169
raise ValidationError (self .message .format (field_names = field_names ))
151
170
@@ -209,7 +228,7 @@ def __call__(self, attrs):
209
228
queryset = self .queryset
210
229
queryset = self .filter_queryset (attrs , queryset )
211
230
queryset = self .exclude_current_instance (attrs , queryset )
212
- if queryset . exists ( ):
231
+ if qs_exists ( queryset ):
213
232
message = self .message .format (date_field = self .date_field )
214
233
raise ValidationError ({self .field : message })
215
234
@@ -234,7 +253,7 @@ def filter_queryset(self, attrs, queryset):
234
253
filter_kwargs ['%s__day' % self .date_field_name ] = date .day
235
254
filter_kwargs ['%s__month' % self .date_field_name ] = date .month
236
255
filter_kwargs ['%s__year' % self .date_field_name ] = date .year
237
- return queryset . filter ( ** filter_kwargs )
256
+ return qs_filter ( queryset , ** filter_kwargs )
238
257
239
258
240
259
class UniqueForMonthValidator (BaseUniqueForValidator ):
@@ -247,7 +266,7 @@ def filter_queryset(self, attrs, queryset):
247
266
filter_kwargs = {}
248
267
filter_kwargs [self .field_name ] = value
249
268
filter_kwargs ['%s__month' % self .date_field_name ] = date .month
250
- return queryset . filter ( ** filter_kwargs )
269
+ return qs_filter ( queryset , ** filter_kwargs )
251
270
252
271
253
272
class UniqueForYearValidator (BaseUniqueForValidator ):
@@ -260,4 +279,4 @@ def filter_queryset(self, attrs, queryset):
260
279
filter_kwargs = {}
261
280
filter_kwargs [self .field_name ] = value
262
281
filter_kwargs ['%s__year' % self .date_field_name ] = date .year
263
- return queryset . filter ( ** filter_kwargs )
282
+ return qs_filter ( queryset , ** filter_kwargs )
0 commit comments