Code

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

File add-1to1-backref-cache.patch, 3.5 KB (added by shai, 2 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))