Opened 16 years ago

Closed 16 years ago

#7173 closed (fixed)

Reverse of OneToOneField relationships are being cached improperly, returning wrong model class

Reported by: George Vilches Owned by: nobody
Component: Core (Other) Version: dev
Severity: Keywords: 121, related_name, cache
Cc: m.gajda@…, anossov@…, michal@… Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

When following a reverse relationship, the cache where the objects pulled from the database are stored is not using the proper variable name for storing the reverse result. Two models both related to the original model will cause the relation to be overwritten on the second access, assuming that both models are related to the original model by the same name (and therefore are forced by the forward relationship to have a related_name attribute to distinguish them).

The solution, provided by Travis Terry (patch attached), is to cache by using the related_name instead of the field name.

Here's an example:

class Test4(models.Model):
    b = models.CharField(max_length=10)
    
    
class Test5(models.Model):
    c = models.CharField(max_length=10)
    f2 = models.OneToOneField(Test4, related_name='first_link')
    

class Test6(models.Model):
    d = models.CharField(max_length=10)
    f2 = models.OneToOneField(Test4, related_name='second_link')
    

# This shows how to trigger the error.
t4 = Test4.objects.create(b='Thing1')
t5 = Test5.objects.create(c='obj1', f2=t4)
t6 = Test6.objects.create(d='obj2', f2=t4)

a = Test4.objects.get(pk=t4.id)

Here's what you get:

>>> print a.first_link
Test5 object
>>> print a.first_link.c
obj1
>>> print a.second_link
Test6 object
>>> print a.second_link.d
Traceback (most recent call last):
...
AttributeError: 'Test5' object has no attribute 'd'

Here's what you should get:

>>> print a.first_link
Test5 object
>>> print a.first_link.c
obj1
>>> print a.second_link
Test6 object
>>> print a.second_link.d
obj2

Attachments (1)

related_cache_r7513.patch (567 bytes ) - added by George Vilches 16 years ago.
Patch from Travis Terry using related_name for the cache field name.

Download all attachments as: .zip

Change History (8)

by George Vilches, 16 years ago

Attachment: related_cache_r7513.patch added

Patch from Travis Terry using related_name for the cache field name.

comment:1 by George Vilches, 16 years ago

Component: UncategorizedCore framework
Has patch: set

comment:2 by George Vilches, 16 years ago

Keywords: 121 related_name cache added

comment:3 by anonymous, 16 years ago

Cc: m.gajda@… added

comment:4 by Pavel Anossov, 16 years ago

Cc: anossov@… added

comment:5 by Michał Sałaban, 16 years ago

Cc: michal@… added

comment:6 by Ilya Semenov, 16 years ago

Triage Stage: UnreviewedReady for checkin

Just traced the same problem and came up with the same solution.
More minimalistic example below (I believe it's easier to understand):

class A(Model):
        pass

class B(Model):
        a = OneToOneField(A)

class C(Model):
        a = OneToOneField(A)

a = A()
a.save()
b = B(a=a)
c = C(a=a)
b.save()
c.save()

a = A.objects.get()
a.b # <B: B object>
a.c # <B: B object> -- WRONG!

I'm marking it as ready for checkin, unless we need regression tests.

comment:7 by Russell Keith-Magee, 16 years ago

Resolution: fixed
Status: newclosed

(In [7561]) Fixed #7173 -- Corrected the caching of objects in reverse OneToOne relationships. Thanks, Travis Terry.

Note: See TracTickets for help on using tickets.
Back to Top