@@ -182,14 +182,29 @@ def __get__(self, instance, instance_type=None):
182
182
def __set__ (self , instance , value ):
183
183
if instance is None :
184
184
raise AttributeError , "%s must be accessed via instance" % self .related .opts .object_name
185
+
186
+ # The similarity of the code below to the code in
187
+ # ReverseSingleRelatedObjectDescriptor is annoying, but there's a bunch
188
+ # of small differences that would make a common base class convoluted.
189
+
190
+ # If null=True, we can assign null here, but otherwise the value needs
191
+ # to be an instance of the related class.
192
+ if value is None and self .related .field .null == False :
193
+ raise ValueError ('Cannot assign None: "%s.%s" does not allow null values.' %
194
+ (instance ._meta .object_name , self .related .get_accessor_name ()))
195
+ elif value is not None and not isinstance (value , self .related .model ):
196
+ raise ValueError ('Cannot assign "%r": "%s.%s" must be a "%s" instance.' %
197
+ (value , instance ._meta .object_name ,
198
+ self .related .get_accessor_name (), self .related .opts .object_name ))
199
+
185
200
# Set the value of the related field
186
201
setattr (value , self .related .field .rel .get_related_field ().attname , instance )
187
202
188
- # Clear the cache, if it exists
189
- try :
190
- delattr ( value , self . related . field . get_cache_name ())
191
- except AttributeError :
192
- pass
203
+ # Since we already know what the related object is, seed the related
204
+ # object caches now, too. This avoids another db hit if you get the
205
+ # object you just set.
206
+ setattr ( instance , self . cache_name , value )
207
+ setattr ( value , self . related . field . get_cache_name (), instance )
193
208
194
209
class ReverseSingleRelatedObjectDescriptor (object ):
195
210
# This class provides the functionality that makes the related-object
@@ -225,18 +240,28 @@ def __get__(self, instance, instance_type=None):
225
240
def __set__ (self , instance , value ):
226
241
if instance is None :
227
242
raise AttributeError , "%s must be accessed via instance" % self ._field .name
243
+
244
+ # If null=True, we can assign null here, but otherwise the value needs
245
+ # to be an instance of the related class.
246
+ if value is None and self .field .null == False :
247
+ raise ValueError ('Cannot assign None: "%s.%s" does not allow null values.' %
248
+ (instance ._meta .object_name , self .field .name ))
249
+ elif value is not None and not isinstance (value , self .field .rel .to ):
250
+ raise ValueError ('Cannot assign "%r": "%s.%s" must be a "%s" instance.' %
251
+ (value , instance ._meta .object_name ,
252
+ self .field .name , self .field .rel .to ._meta .object_name ))
253
+
228
254
# Set the value of the related field
229
255
try :
230
256
val = getattr (value , self .field .rel .get_related_field ().attname )
231
257
except AttributeError :
232
258
val = None
233
259
setattr (instance , self .field .attname , val )
234
260
235
- # Clear the cache, if it exists
236
- try :
237
- delattr (instance , self .field .get_cache_name ())
238
- except AttributeError :
239
- pass
261
+ # Since we already know what the related object is, seed the related
262
+ # object cache now, too. This avoids another db hit if you get the
263
+ # object you just set.
264
+ setattr (instance , self .field .get_cache_name (), value )
240
265
241
266
class ForeignRelatedObjectsDescriptor (object ):
242
267
# This class provides the functionality that makes the related-object
0 commit comments