@@ -25,6 +25,10 @@ def __init__(self, queryset, message=None):
25
25
self .message = message or self .message
26
26
27
27
def set_context (self , serializer_field ):
28
+ """
29
+ This hook is called by the serializer instance,
30
+ prior to the validation call being made.
31
+ """
28
32
# Determine the underlying model field name. This may not be the
29
33
# same as the serializer field name if `source=<>` is set.
30
34
self .field_name = serializer_field .source_attrs [0 ]
@@ -54,6 +58,7 @@ class UniqueTogetherValidator:
54
58
Should be applied to the serializer class, not to an individual field.
55
59
"""
56
60
message = _ ('The fields {field_names} must make a unique set.' )
61
+ missing_message = _ ('This field is required.' )
57
62
58
63
def __init__ (self , queryset , fields , message = None ):
59
64
self .queryset = queryset
@@ -62,17 +67,49 @@ def __init__(self, queryset, fields, message=None):
62
67
self .message = message or self .message
63
68
64
69
def set_context (self , serializer ):
70
+ """
71
+ This hook is called by the serializer instance,
72
+ prior to the validation call being made.
73
+ """
65
74
# Determine the existing instance, if this is an update operation.
66
75
self .instance = getattr (serializer , 'instance' , None )
67
76
68
- def __call__ (self , attrs ):
69
- # Ensure uniqueness.
77
+ def enforce_required_fields (self , attrs ):
78
+ """
79
+ The `UniqueTogetherValidator` always forces an implied 'required'
80
+ state on the fields it applies to.
81
+ """
82
+ missing = dict ([
83
+ (field_name , self .missing_message )
84
+ for field_name in self .fields
85
+ if field_name not in attrs
86
+ ])
87
+ if missing :
88
+ raise ValidationError (missing )
89
+
90
+ def filter_queryset (self , attrs , queryset ):
91
+ """
92
+ Filter the queryset to all instances matching the given attributes.
93
+ """
70
94
filter_kwargs = dict ([
71
95
(field_name , attrs [field_name ]) for field_name in self .fields
72
96
])
73
- queryset = self .queryset .filter (** filter_kwargs )
97
+ return queryset .filter (** filter_kwargs )
98
+
99
+ def exclude_current_instance (self , attrs , queryset ):
100
+ """
101
+ If an instance is being updated, then do not include
102
+ that instance itself as a uniqueness conflict.
103
+ """
74
104
if self .instance is not None :
75
- queryset = queryset .exclude (pk = self .instance .pk )
105
+ return queryset .exclude (pk = self .instance .pk )
106
+ return queryset
107
+
108
+ def __call__ (self , attrs ):
109
+ self .enforce_required_fields (attrs )
110
+ queryset = self .queryset
111
+ queryset = self .filter_queryset (attrs , queryset )
112
+ queryset = self .exclude_current_instance (attrs , queryset )
76
113
if queryset .exists ():
77
114
field_names = ', ' .join (self .fields )
78
115
raise ValidationError (self .message .format (field_names = field_names ))
@@ -87,6 +124,7 @@ def __repr__(self):
87
124
88
125
class BaseUniqueForValidator :
89
126
message = None
127
+ missing_message = _ ('This field is required.' )
90
128
91
129
def __init__ (self , queryset , field , date_field , message = None ):
92
130
self .queryset = queryset
@@ -95,22 +133,47 @@ def __init__(self, queryset, field, date_field, message=None):
95
133
self .message = message or self .message
96
134
97
135
def set_context (self , serializer ):
136
+ """
137
+ This hook is called by the serializer instance,
138
+ prior to the validation call being made.
139
+ """
98
140
# Determine the underlying model field names. These may not be the
99
141
# same as the serializer field names if `source=<>` is set.
100
142
self .field_name = serializer .fields [self .field ].source_attrs [0 ]
101
143
self .date_field_name = serializer .fields [self .date_field ].source_attrs [0 ]
102
144
# Determine the existing instance, if this is an update operation.
103
145
self .instance = getattr (serializer , 'instance' , None )
104
146
105
- def get_filter_kwargs (self , attrs ):
106
- raise NotImplementedError ('`get_filter_kwargs` must be implemented.' )
147
+ def enforce_required_fields (self , attrs ):
148
+ """
149
+ The `UniqueFor<Range>Validator` classes always force an implied
150
+ 'required' state on the fields they are applied to.
151
+ """
152
+ missing = dict ([
153
+ (field_name , self .missing_message )
154
+ for field_name in [self .field , self .date_field ]
155
+ if field_name not in attrs
156
+ ])
157
+ if missing :
158
+ raise ValidationError (missing )
107
159
108
- def __call__ (self , attrs ):
109
- filter_kwargs = self . get_filter_kwargs ( attrs )
160
+ def filter_queryset (self , attrs , queryset ):
161
+ raise NotImplementedError ( '`filter_queryset` must be implemented.' )
110
162
111
- queryset = self .queryset .filter (** filter_kwargs )
163
+ def exclude_current_instance (self , attrs , queryset ):
164
+ """
165
+ If an instance is being updated, then do not include
166
+ that instance itself as a uniqueness conflict.
167
+ """
112
168
if self .instance is not None :
113
- queryset = queryset .exclude (pk = self .instance .pk )
169
+ return queryset .exclude (pk = self .instance .pk )
170
+ return queryset
171
+
172
+ def __call__ (self , attrs ):
173
+ self .enforce_required_fields (attrs )
174
+ queryset = self .queryset
175
+ queryset = self .filter_queryset (attrs , queryset )
176
+ queryset = self .exclude_current_instance (attrs , queryset )
114
177
if queryset .exists ():
115
178
message = self .message .format (date_field = self .date_field )
116
179
raise ValidationError ({self .field : message })
@@ -127,7 +190,7 @@ def __repr__(self):
127
190
class UniqueForDateValidator (BaseUniqueForValidator ):
128
191
message = _ ('This field must be unique for the "{date_field}" date.' )
129
192
130
- def get_filter_kwargs (self , attrs ):
193
+ def filter_queryset (self , attrs , queryset ):
131
194
value = attrs [self .field ]
132
195
date = attrs [self .date_field ]
133
196
@@ -136,30 +199,30 @@ def get_filter_kwargs(self, attrs):
136
199
filter_kwargs ['%s__day' % self .date_field_name ] = date .day
137
200
filter_kwargs ['%s__month' % self .date_field_name ] = date .month
138
201
filter_kwargs ['%s__year' % self .date_field_name ] = date .year
139
- return filter_kwargs
202
+ return queryset . filter ( ** filter_kwargs )
140
203
141
204
142
205
class UniqueForMonthValidator (BaseUniqueForValidator ):
143
206
message = _ ('This field must be unique for the "{date_field}" month.' )
144
207
145
- def get_filter_kwargs (self , attrs ):
208
+ def filter_queryset (self , attrs , queryset ):
146
209
value = attrs [self .field ]
147
210
date = attrs [self .date_field ]
148
211
149
212
filter_kwargs = {}
150
213
filter_kwargs [self .field_name ] = value
151
214
filter_kwargs ['%s__month' % self .date_field_name ] = date .month
152
- return filter_kwargs
215
+ return queryset . filter ( ** filter_kwargs )
153
216
154
217
155
218
class UniqueForYearValidator (BaseUniqueForValidator ):
156
219
message = _ ('This field must be unique for the "{date_field}" year.' )
157
220
158
- def get_filter_kwargs (self , attrs ):
221
+ def filter_queryset (self , attrs , queryset ):
159
222
value = attrs [self .field ]
160
223
date = attrs [self .date_field ]
161
224
162
225
filter_kwargs = {}
163
226
filter_kwargs [self .field_name ] = value
164
227
filter_kwargs ['%s__year' % self .date_field_name ] = date .year
165
- return filter_kwargs
228
+ return queryset . filter ( ** filter_kwargs )
0 commit comments