Django

Code

Ticket #6886: 6886.patch

File 6886.patch, 3.8 kB (added by jacob, 6 months ago)
  • a/django/db/models/fields/related.py

    old new  
    225225    def __set__(self, instance, value): 
    226226        if instance is None: 
    227227            raise AttributeError, "%s must be accessed via instance" % self._field.name 
     228         
     229        # If null=True, we can assign null here, but otherwise the value needs 
     230        # to be an instance of the related class. 
     231        if value is None and self.field.null == False: 
     232            raise ValueError('Cannot assign None: "%s.%s" does not allow null values.' % 
     233                                (instance._meta.object_name, self.field.name)) 
     234        elif value is not None and not isinstance(value, self.field.rel.to): 
     235            raise ValueError('Cannot assign "%r": "%s.%s" must be a "%s" instance.' % 
     236                                (value, instance._meta.object_name,  
     237                                 self.field.name, self.field.rel.to._meta.object_name)) 
     238         
    228239        # Set the value of the related field 
    229240        try: 
    230241            val = getattr(value, self.field.rel.get_related_field().attname) 
     
    232243            val = None 
    233244        setattr(instance, self.field.attname, val) 
    234245 
    235         # Clear the cache, if it exists 
    236         try: 
    237             delattr(instance, self.field.get_cache_name()) 
    238         except AttributeError: 
    239             pass 
     246        # Since we already know what the related object is, seed the related 
     247        # object cache now, too. This avoids another db hit if you get the  
     248        # object you just set. 
     249        setattr(instance, self.field.get_cache_name(), value) 
    240250 
    241251class ForeignRelatedObjectsDescriptor(object): 
    242252    # This class provides the functionality that makes the related-object 
  • a/tests/regressiontests/many_to_one_regress/models.py

    old new  
    2525 
    2626 
    2727__test__ = {'API_TESTS':""" 
    28 >>> Third.AddManipulator().save(dict(id='3', name='An example', another=None))  
     28>>> Third.objects.create(id='3', name='An example') 
    2929<Third: Third object> 
    3030>>> parent = Parent(name = 'fred') 
    3131>>> parent.save() 
    32 >>> Child.AddManipulator().save(dict(name='bam-bam', parent=parent.id)
     32>>> Child.objects.create(name='bam-bam', parent=parent
    3333<Child: Child object> 
     34 
     35# 
     36# Tests of ForeignKey assignment and the related-object cache (see #6886) 
     37# 
     38>>> p = Parent.objects.create(name="Parent") 
     39>>> c = Child.objects.create(name="Child", parent=p) 
     40 
     41# Look up the object again so that we get a "fresh" object 
     42>>> c = Child.objects.get(name="Child") 
     43>>> p = c.parent 
     44 
     45# Accessing the related object again returns the exactly same object 
     46>>> c.parent is p 
     47True 
     48 
     49# But if we kill the cache, we get a new object 
     50>>> del c._parent_cache 
     51>>> c.parent is p 
     52False 
     53 
     54# Here's where the changes discussed in #6886 (and committed in [XXXX]) start: 
     55 
     56# Assigning a new object results in that object getting cached immediately 
     57>>> p2 = Parent.objects.create(name="Parent 2") 
     58>>> c.parent = p2 
     59>>> c.parent is p2 
     60True 
     61 
     62# Assigning None fails: Child.parent is null=False 
     63>>> c.parent = None 
     64Traceback (most recent call last): 
     65    ... 
     66ValueError: Cannot assign None: "Child.parent" does not allow null values. 
     67 
     68# You also can't assign an object of the wrong type here 
     69>>> c.parent = First(id=1, second=1) 
     70Traceback (most recent call last): 
     71    ... 
     72ValueError: Cannot assign "<First: First object>": "Child.parent" must be a "Parent" instance. 
     73 
    3474"""}