Ticket #6886: 6886.patch

File 6886.patch, 3.8 KB (added by jacob, 7 years ago)
  • django/db/models/fields/related.py

    diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
    index e3677c2..e6f651d 100644
    a b class ReverseSingleRelatedObjectDescriptor(object): 
    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)
    class ReverseSingleRelatedObjectDescriptor(object): 
    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
  • tests/regressiontests/many_to_one_regress/models.py

    diff --git a/tests/regressiontests/many_to_one_regress/models.py b/tests/regressiontests/many_to_one_regress/models.py
    index 57bbcd8..012ccbe 100644
    a b class Child(models.Model): 
    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"""}
Back to Top