Changeset 7574
- Timestamp:
- 06/04/08 19:39:32 (3 months ago)
- Files:
-
- django/trunk/django/db/models/fields/related.py (modified) (3 diffs)
- django/trunk/tests/modeltests/one_to_one/models.py (modified) (1 diff)
- django/trunk/tests/regressiontests/many_to_one_regress/models.py (modified) (2 diffs)
- django/trunk/tests/regressiontests/one_to_one_regress/models.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/trunk/django/db/models/fields/related.py
r7561 r7574 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 exists189 try:190 delattr(value, self.related.field.get_cache_name())191 except AttributeError:192 pass203 # 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): … … 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: … … 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): django/trunk/tests/modeltests/one_to_one/models.py
r7477 r7574 81 81 <Place: Ace Hardware the place> 82 82 83 # Set the place back again, using assignment in the reverse direction. Need to 84 # reload restaurant object first, because the reverse set can't update the 85 # existing restaurant instance 83 # Set the place back again, using assignment in the reverse direction. 86 84 >>> p1.restaurant = r 87 >>> r.save()88 85 >>> p1.restaurant 89 86 <Restaurant: Demon Dogs the restaurant> django/trunk/tests/regressiontests/many_to_one_regress/models.py
r5876 r7574 1 """ 2 Regression tests for a few FK bugs: #1578, #6886 3 """ 4 1 5 from django.db import models 2 6 … … 26 30 27 31 __test__ = {'API_TESTS':""" 28 >>> Third. AddManipulator().save(dict(id='3', name='An example', another=None))32 >>> Third.objects.create(id='3', name='An example') 29 33 <Third: Third object> 30 34 >>> parent = Parent(name = 'fred') 31 35 >>> parent.save() 32 >>> Child. AddManipulator().save(dict(name='bam-bam', parent=parent.id))36 >>> Child.objects.create(name='bam-bam', parent=parent) 33 37 <Child: Child object> 38 39 # 40 # Tests of ForeignKey assignment and the related-object cache (see #6886) 41 # 42 >>> p = Parent.objects.create(name="Parent") 43 >>> c = Child.objects.create(name="Child", parent=p) 44 45 # Look up the object again so that we get a "fresh" object 46 >>> c = Child.objects.get(name="Child") 47 >>> p = c.parent 48 49 # Accessing the related object again returns the exactly same object 50 >>> c.parent is p 51 True 52 53 # But if we kill the cache, we get a new object 54 >>> del c._parent_cache 55 >>> c.parent is p 56 False 57 58 # Assigning a new object results in that object getting cached immediately 59 >>> p2 = Parent.objects.create(name="Parent 2") 60 >>> c.parent = p2 61 >>> c.parent is p2 62 True 63 64 # Assigning None fails: Child.parent is null=False 65 >>> c.parent = None 66 Traceback (most recent call last): 67 ... 68 ValueError: Cannot assign None: "Child.parent" does not allow null values. 69 70 # You also can't assign an object of the wrong type here 71 >>> c.parent = First(id=1, second=1) 72 Traceback (most recent call last): 73 ... 74 ValueError: Cannot assign "<First: First object>": "Child.parent" must be a "Parent" instance. 75 34 76 """} django/trunk/tests/regressiontests/one_to_one_regress/models.py
r7561 r7574 51 51 >>> p1.bar 52 52 <Bar: Demon Dogs the bar> 53 54 # 55 # Regression test for #6886 (the related-object cache) 56 # 57 58 # Look up the objects again so that we get "fresh" objects 59 >>> p = Place.objects.get(name="Demon Dogs") 60 >>> r = p.restaurant 61 62 # Accessing the related object again returns the exactly same object 63 >>> p.restaurant is r 64 True 65 66 # But if we kill the cache, we get a new object 67 >>> del p._restaurant_cache 68 >>> p.restaurant is r 69 False 70 71 # Reassigning the Restaurant object results in an immediate cache update 72 # We can't use a new Restaurant because that'll violate one-to-one, but 73 # with a new *instance* the is test below will fail if #6886 regresses. 74 >>> r2 = Restaurant.objects.get(pk=r.pk) 75 >>> p.restaurant = r2 76 >>> p.restaurant is r2 77 True 78 79 # Assigning None fails: Place.restaurant is null=False 80 >>> p.restaurant = None 81 Traceback (most recent call last): 82 ... 83 ValueError: Cannot assign None: "Place.restaurant" does not allow null values. 84 85 # You also can't assign an object of the wrong type here 86 >>> p.restaurant = p 87 Traceback (most recent call last): 88 ... 89 ValueError: Cannot assign "<Place: Demon Dogs the place>": "Place.restaurant" must be a "Restaurant" instance. 90 53 91 """}
