Opened 16 years ago

Closed 16 years ago

Last modified 13 years ago

#9023 closed (fixed)

OneToOneField delete() problem

Reported by: TheShark Owned by: nobody
Component: Database layer (models, ORM) Version: dev
Severity: Keywords: OneToOneField delete cascade
Cc: juriejanbotha@…, David Larlet Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

Accessing the automatic reverse relationship on an instance (w.sprocket in the following example) causes some type of caching to happen which results in my Sprocket getting caught up in a cascade delete() of a Widget erroneously. The following example works without the print statement, and my Sprocket s exists at the end. If I 'print w.sprocket' before clearing the relationship, my Sprocket gets deleted along with my Widget when it shouldn't.

class Widget(models.Model):
    name = models.CharField(max_length=10)

    def __unicode__(self):
        return u'%s(%s)'%(self.name,self.pk)

class Sprocket(models.Model):
    name = models.CharField(max_length=10)
    w = models.OneToOneField(Widget, null=True, blank=True)

    def __unicode__(self):
        return u'%s(%s)'%(self.name,self.pk)

And the following sequence of operations:

w=Widget(name="Some Widget")
w.save()
s=Sprocket(name="Some Sprocket")
s.w=w
s.save()

assert Widget.objects.all().count()==1
assert Sprocket.objects.all().count()==1

#print w.sprocket
s.w=None
s.save()
w.delete()

assert Widget.objects.all().count()==0
assert Sprocket.objects.all().count()==1

Attachments (3)

onetoone_cache_clear.diff (2.2 KB ) - added by juriejan 16 years ago.
First sloppy version of the patch (needs feedback)
9023-test.diff (2.7 KB ) - added by Andy Durdin 16 years ago.
Regression test to exemplify the bug.
9023.diff (3.3 KB ) - added by Jacob 16 years ago.

Download all attachments as: .zip

Change History (16)

comment:1 by Ivan Giuliani, 16 years ago

Component: UncategorizedDatabase layer (models, ORM)

comment:2 by Jacob, 16 years ago

milestone: 1.1
Triage Stage: UnreviewedAccepted

by juriejan, 16 years ago

Attachment: onetoone_cache_clear.diff added

First sloppy version of the patch (needs feedback)

comment:3 by juriejan, 16 years ago

Cc: juriejanbotha@… oliver@… cmlenz@… gary.wilson@… mhf@… tom@… django@… brosner@… tom85@… dan.popovici@… django@… lnielsen@… to patrick.lauber@… mpjung@… django@… remco@… barbuzaster@… flavio.curella@… semente@… stephane@… added
Has patch: set
Needs tests: set
Owner: changed from nobody to juriejan
Patch needs improvement: set

I've created a sloppy patch to fix this issue. It's my first whack at working on the DB layer (or making a serious contribution) so please bear with me.

I figured that we need a reference to the SingleRelatedObjectDescriptor that is related to the ReverseSingleRelatedObjectDescriptor for the OnToOne field so that we can access it from the 'set' method of the ReverseSingleRelatedObjectDescriptor to remove the cache from the object that the relation previously pointed to. So I added the 'related_object_descriptor' attribute to the RelatedField object. Not sure if I should rather add the attribute in a more appropriate place, like in RelatedObject or ReverseSingleRelatedObjectDescriptor itself.

comment:4 by Oliver Beattie, 16 years ago

Cc: oliver@… removed

I didn't subscribe to this :\

comment:5 by juriejan, 16 years ago

Cc: cmlenz@… gary.wilson@… mhf@… tom@… django@… brosner@… tom85@… dan.popovici@… django@… lnielsen@… to patrick.lauber@… mpjung@… django@… remco@… barbuzaster@… flavio.curella@… semente@… stephane@… removed

comment:6 by juriejan, 16 years ago

Version: 1.0SVN

by Andy Durdin, 16 years ago

Attachment: 9023-test.diff added

Regression test to exemplify the bug.

comment:7 by Andy Durdin, 16 years ago

Needs tests: unset

by Jacob, 16 years ago

Attachment: 9023.diff added

comment:8 by Jacob, 16 years ago

Working patch attached. I'm not sure I'm 100% happy with the approach: in some cases it can make obj.o2o = None perform a database hit, which is annoying and counter-intuitive.

comment:9 by Alex Gaynor, 16 years ago

We can remove the extra SQL query by essentially unrolling the getattr call. We only need to update the related object if the obj is already loaded, so can check this by poking around in dict.

comment:10 by juriejan, 16 years ago

Owner: changed from juriejan to nobody

comment:11 by David Larlet, 16 years ago

Cc: David Larlet added

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

Resolution: fixed
Status: newclosed

(In [11009]) Fixed #9023 -- Corrected a problem where cached attribute values would cause a delete to cascade to a related object even when the relationship had been set to None. Thanks to TheShark for the report and test case, and to juriejan and Jacob for their work on the patch.

comment:13 by Jacob, 13 years ago

milestone: 1.1

Milestone 1.1 deleted

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