Ticket #17565: add-1to1-backref-cache.patch

File add-1to1-backref-cache.patch, 3.5 KB (added by shai, 3 years ago)

And here is the fix. This makes the tests attached above pass, and doesn't seem to break any others. Also, a similar solution has been exercised with no problems in a Django-1.2 based solution.

  • django/db/models/fields/related.py

    diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
    index 848fd6e..414f3f6 100644
    a b class RelatedField(object): 
    220220        return self.rel.related_name or self.opts.object_name.lower()
    221221
    222222class SingleRelatedObjectDescriptor(object):
    223     # This class provides the functionality that makes the related-object
    224     # managers available as attributes on a model class, for fields that have
     223    # This class provides the functionality that makes the related objects
     224    # (not managers) available as attributes on a model class, for fields that have
    225225    # a single "remote" value, on the class pointed to by a related field.
    226226    # In the example "place.restaurant", the restaurant attribute is a
    227227    # SingleRelatedObjectDescriptor instance.
    class SingleRelatedObjectDescriptor(object): 
    254254            params = {'%s__pk' % self.related.field.name: instance._get_pk_val()}
    255255            rel_obj = self.get_query_set(instance=instance).get(**params)
    256256            setattr(instance, self.cache_name, rel_obj)
     257            # Also cache a back-reference to me on the related object
     258            # (this is only used in one-to-one relations)
     259            if rel_obj:
     260                cache_name = self.related.field.get_cache_name()
     261                if not getattr(rel_obj, cache_name, None):
     262                    setattr(rel_obj, cache_name, instance)
    257263            return rel_obj
    258264
    259265    def __set__(self, instance, value):
    class ReverseSingleRelatedObjectDescriptor(object): 
    409415        # object you just set.
    410416        setattr(instance, self.field.get_cache_name(), value)
    411417
     418class CachingReverseSingleRelatedObjectDescriptor(ReverseSingleRelatedObjectDescriptor):
     419    """
     420    This is a variation on ReverseSingleRelatedObjectDescriptor, which makes sure
     421    a reverse back-reference (which is a forward reference) is cached on the target
     422    of a reverse relation access. This is useful for one-to-one relations, making sure
     423    that if
     424     
     425    class A(model):
     426        pass
     427    class B(model):
     428        a = OneToOneField(A)
     429    a1 = A.objects.get(...)
     430   
     431    then
     432   
     433    a1.b.a is a1
     434    """ 
     435    def __get__(self, instance, instance_type=None):
     436        myclass = CachingReverseSingleRelatedObjectDescriptor # ridiculously long name...
     437        rel_obj = super(myclass, self).__get__(instance, instance_type)
     438        if rel_obj:
     439            cache_name = self.field.related.get_cache_name()
     440            if not getattr(rel_obj, cache_name, None):
     441                setattr (rel_obj, cache_name, instance)
     442        return rel_obj
     443   
     444
    412445class ForeignRelatedObjectsDescriptor(object):
    413446    # This class provides the functionality that makes the related-object
    414447    # managers available as attributes on a model class, for fields that have
    class OneToOneField(ForeignKey): 
    10341067        kwargs['unique'] = True
    10351068        super(OneToOneField, self).__init__(to, to_field, OneToOneRel, **kwargs)
    10361069
     1070    def contribute_to_class(self, cls, name):
     1071        # Ancestors do all sorts of stuff
     1072        super(OneToOneField, self).contribute_to_class(cls, name)
     1073        # ... but they choose the wrong implementation for the attribute
     1074        setattr(cls, self.name, CachingReverseSingleRelatedObjectDescriptor(self))
     1075
    10371076    def contribute_to_related_class(self, cls, related):
    10381077        setattr(cls, related.get_accessor_name(),
    10391078                SingleRelatedObjectDescriptor(related))
Back to Top