Opened 15 years ago

Closed 14 years ago

Last modified 12 years ago

#9741 closed (duplicate)

generic relations: reverse chain of references not followed fully on delete

Reported by: Éric St-Jean Owned by: aljosa
Component: Contrib apps Version: 1.0
Severity: Keywords: genericrelation genericforeignkey
Cc: eric@…, malini@…, martin@… Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

If i have an object c, which has a genericforeignkey relation to b, which in turn has a genericforeignkey relation to a, and i delete a, b does get deleted, but not c. If i were to delete b, c does get deleted.

If these were foreign keys, the chain of reverse relations would get properly deleted, but for genericrelations, it only goes back 1 link.

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

class A(models.Model):
    t = models.CharField(max_length=50)
    gr_b_reverse = generic.GenericRelation('GR_B')

class FK_B(models.Model):
    """An object with a foreign key, that we'll point to an A"""
    fk = models.ForeignKey('A')

class FK_C(models.Model):
    """An object with a foreign key, that we'll point to a FK_B"""
    fk = models.ForeignKey('FK_B')

class GR_B(models.Model):
    """An object with a generic key, that we'll point to an A"""
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_obj = generic.GenericForeignKey(ct_field="content_type",
                                            fk_field="object_id")
    gr_c_reverse = generic.GenericRelation('GR_C')

class GR_C(models.Model):
    """An object with a generic key, that we'll point to a GR_B
    
    # test proper deletion of gr_c pointing to gr_b if we delete gr_b
    >>> a=A.objects.create(t='foo')
    >>> gr_b=GR_B.objects.create(content_obj=a)
    >>> gr_c=GR_C.objects.create(content_obj=gr_b)
    >>> gr_b.delete()
    >>> print A.objects.all(), GR_B.objects.all(), GR_C.objects.all()
    [<A: A object>] [] []
    >>> a.delete()
    
    # test proper deletion of gr_b pointing to a if we delete a
    >>> a=A.objects.create(t='foo')
    >>> gr_b=GR_B.objects.create(content_obj=a)
    >>> a.delete()
    >>> print A.objects.all(), GR_B.objects.all()
    [] []
    
    # test proper deletion of fk_b pointing to a, fk_c pointing to b, gr_b pointing
    # to a, and gr_c pointing to gr_b if we delete a
    >>> a=A.objects.create(t='foo')
    >>> gr_b=GR_B.objects.create(content_obj=a)
    >>> gr_c=GR_C.objects.create(content_obj=gr_b)
    >>> fk_b=FK_B.objects.create(fk=a)
    >>> fk_c=FK_C.objects.create(fk=fk_b)
    >>> print A.objects.all(), GR_B.objects.all(), GR_C.objects.all(), FK_B.objects.all(), FK_C.objects.all()
    [<A: A object>] [<GR_B: GR_B object>] [<GR_C: GR_C object>] [<FK_B: FK_B object>] [<FK_C: FK_C object>]
    >>> a.delete()
    >>> print A.objects.all(), GR_B.objects.all(), GR_C.objects.all(), FK_B.objects.all(), FK_C.objects.all()
    [] [] [] [] []
    
    # FAIL! we instead get:
    # [] [] [<GR_C: GR_C object>] [] []
    """
    
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_obj = generic.GenericForeignKey(ct_field="content_type",
                                            fk_field="object_id")

Attachments (1)

gr_test.tgz (2.7 KB ) - added by Éric St-Jean 15 years ago.
full project tree for test app

Download all attachments as: .zip

Change History (11)

by Éric St-Jean, 15 years ago

Attachment: gr_test.tgz added

full project tree for test app

comment:1 by Éric St-Jean, 15 years ago

Cc: eric@… added; eriic@… removed

comment:2 by Éric St-Jean, 15 years ago

test output:

eric@pluto:~/akoha/sandbox/gr_test$ ./manage.py test gr_test_app
Creating test database...
Creating table auth_permission
Creating table auth_group
Creating table auth_user
Creating table auth_message
Creating table django_content_type
Creating table django_session
Creating table django_site
Creating table gr_test_app_a
Creating table gr_test_app_fk_b
Creating table gr_test_app_fk_c
Creating table gr_test_app_gr_b
Creating table gr_test_app_gr_c
Installing index for auth.Permission model
Installing index for auth.Message model
Installing index for gr_test_app.FK_B model
Installing index for gr_test_app.FK_C model
Installing index for gr_test_app.GR_B model
Installing index for gr_test_app.GR_C model
F
======================================================================
FAIL: Doctest: gr_test_app.models.GR_C
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/eric/django/django_src/django/test/_doctest.py", line 2180, in runTest
    raise self.failureException(self.format_failure(new.getvalue()))
AssertionError: Failed doctest test for gr_test_app.models.GR_C
  File "/home/eric/akoha/sandbox/gr_test/gr_test_app/models.py", line 25, in GR_C

----------------------------------------------------------------------
File "/home/eric/akoha/sandbox/gr_test/gr_test_app/models.py", line 54, in gr_test_app.models.GR_C
Failed example:
    print A.objects.all(), GR_B.objects.all(), GR_C.objects.all(), FK_B.objects.all(), FK_C.objects.all()
Expected:
    [] [] [] [] []
Got:
    [] [] [<GR_C: GR_C object>] [] []


----------------------------------------------------------------------
Ran 1 test in 0.041s

FAILED (failures=1)
Destroying test database...

comment:3 by Éric St-Jean, 15 years ago

python + django version tested with:

Python 2.5.2 (r252:60911, Jul 31 2008, 17:28:52) 
[GCC 4.2.3 (Ubuntu 4.2.3-2ubuntu7)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VERSION
(1, 1, 0, 'alpha', 0)

which is rev 9549.

also tried with (1, 0, 'final') with the exact same result.

MySQL is 5.0.51a-3ubuntu5.4

comment:4 by Jacob, 15 years ago

Triage Stage: UnreviewedAccepted

comment:5 by Jacob, 15 years ago

milestone: 1.1

comment:6 by Jacob, 15 years ago

milestone: 1.11.2

Pushing to 1.2. This is borderline, but at this late stage without a patch we'll have to live with this inconsistency for now. A custom Model.delete() method can be an acceptable workaround in these cases.

comment:7 by aljosa, 15 years ago

Owner: changed from nobody to aljosa

comment:8 by Martin Diers, 14 years ago

Summary: generic relations: reverse chain of references not followed fully yon deletegeneric relations: reverse chain of references not followed fully on delete

comment:9 by Carl Meyer, 14 years ago

Resolution: duplicate
Status: newclosed

Closing as duplicate of #12953; this one was filed first, but the other already has a working patch with tests.

comment:10 by Jacob, 12 years ago

milestone: 1.2

Milestone 1.2 deleted

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