Opened 5 years ago

Closed 5 years ago

Last modified 4 years ago

#14896 closed (fixed)

Delete leads to IntegrityError : bad cascading rule when there's a ManyToManyField pointing to a class having subclasses.

Reported by: tbrizzi Owned by: carljm
Component: Database layer (models, ORM) Version: 1.3-alpha
Severity: Keywords: blocker
Cc: alexkoshelev Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

I'm working on a scientific Django based application. One of its features is to store all researchers of a laboratory and relative contacts like addresses, phones, faxes, websites or emails :

class Contact(Cast):
    """Base class of all type of contacts like Address, Phone, Fax, E-mail or Website."""
    label = models.CharField(max_length=100)
    
    def __unicode__(self):
        return self.label
    
    class Meta :
        ordering = ['label']

class EMail(Contact):
    """A subclass of Contact to store an EMail address."""
    identifier = models.EmailField(verbose_name="address", max_length=256)
    
    def __unicode__(self):
        st = "%s : %s" % (self.label, self.identifier)
        return st
    
    class Meta:
        verbose_name = "e-mail"

class Researcher(models.Model):
    """Anybody working in a ScientificStructure."""
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    photo = models.ImageField(upload_to="uploads/images/photos", null=True, blank=True)
    db_user = models.OneToOneField(User, null=True, blank=True, verbose_name="database user")
    contacts = models.ManyToManyField(Contact, null=True, blank=True)
    notes = models.TextField(null=True, blank=True)
    
    def get_full_name(self):
        """Get the full name of a Researcher, i.e. the combination between its first and last names."""
        return "%s %s" % (self.first_name, self.last_name)
    
    def __unicode__(self):
        return self.get_full_name()
    
    class Meta:
        ordering = ['last_name', 'first_name']

From IPython, I create and link together a Researcher and an EMail address :

In [1]: from helmholtz.people.models import EMail, Researcher

In [2]: researcher = Researcher.objects.create(first_name='Django', last_name='Reinhardt')

In [3]: email = EMail.objects.create(label='django reinhardt email', identifier='django.reinhardt@gmail.com')

In [4]: researcher.contacts.add(email)

When I execute the following piece of code from IPython, the interpreter returns this :


In [5]: email.delete()
---------------------------------------------------------------------------
IntegrityError                            Traceback (most recent call last)

C:\Users\Thierry.NeuroInf-DB_2\<ipython console> in <module>()

D:\Thierry\Projects\django-svn\django\db\models\base.pyc in delete(self, using)
    577         collector = Collector(using=using)
    578         collector.collect([self])
--> 579         collector.delete()
    580
    581     delete.alters_data = True

D:\Thierry\Projects\django-svn\django\db\models\deletion.pyc in decorated(self, *args, **kwargs)
     48             func(self, *args, **kwargs)
     49             if forced_managed:
---> 50                 transaction.commit(using=self.using)
     51             else:
     52                 transaction.commit_unless_managed(using=self.using)

D:\Thierry\Projects\django-svn\django\db\transaction.pyc in commit(using)
    199         using = DEFAULT_DB_ALIAS
    200     connection = connections[using]
--> 201     connection._commit()
    202     set_clean(using=using)
    203

D:\Thierry\Projects\django-svn\django\db\backends\postgresql_psycopg2\base.pyc in _commit(self)
    198         if self.connection is not None:
    199             try:
--> 200                 return self.connection.commit()
    201             except Database.IntegrityError, e:
    202                 raise utils.IntegrityError, utils.IntegrityError(*tuple(e)), sys.exc_info()[2]

IntegrityError: ERROR:  UPDATE or DELETE on table "people_contact" violates foreign key constraint
"people_researcher_contacts_contact_id_fkey" on table "people_researcher_contacts"
DETAIL:  key (id)=(5) is still referenced from table "people_researcher_contacts".

Seems there's a bad cascading rule when there's a many to many field pointing to a class having subclasses.

This error has never happened before Django 1.3 alpha.

Attachments (2)

django-14896.diff (2.0 KB) - added by Alex 5 years ago.
I can't reproduce this issue.
14896-test.diff (2.2 KB) - added by kmtracey 5 years ago.
Test that demonstrates the problem, fails on trunk, passes on 1.2.X

Download all attachments as: .zip

Change History (11)

Changed 5 years ago by Alex

I can't reproduce this issue.

comment:1 Changed 5 years ago by russellm

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Resolution set to worksforme
  • Status changed from new to closed

I concur with Alex. I can't reproduce this using the details you provide, and I can't see an obvious set of circumstances that would cause this bug to occur.

However, it's a very concerning -- the cascading rules have changed in 1.3, so we want to make this really isn't a bug. Confirming this is a high priority issue for 1.3 final. I'm closing worksforme right now, but please reopen it if you can provide any more details.

Ideally, this would come in the form of a modification to Alex's attached test case that fails.

comment:2 Changed 5 years ago by kmtracey

  • Resolution worksforme deleted
  • Status changed from closed to reopened

Error occurs during commit, therefore test method needs to be in a TransactionTestCase in order to see the problem:

--> ./runtests.py --settings=testdb.postgres -v1 delete_regress
Creating test database for alias 'default'...
Creating test database for alias 'other'...
Destroying old test database 'other'...
...E.
======================================================================
ERROR: test_inheritance (regressiontests.delete_regress.tests.DeleteCascadeTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/kmtracey/django/trunk/tests/regressiontests/delete_regress/tests.py", line 119, in test_inheritance
    email.delete()
  File "/home/kmtracey/django/trunk/django/db/models/base.py", line 579, in delete
    collector.delete()
  File "/home/kmtracey/django/trunk/django/db/models/deletion.py", line 50, in decorated
    transaction.commit(using=self.using)
  File "/home/kmtracey/django/trunk/django/db/transaction.py", line 201, in commit
    connection._commit()
  File "/home/kmtracey/django/trunk/django/db/backends/postgresql_psycopg2/base.py", line 200, in _commit
    return self.connection.commit()
IntegrityError: update or delete on table "delete_regress_contact" violates foreign key constraint "delete_regress_researcher_contacts_contact_id_fkey" on table "delete_regress_researcher_contacts"
DETAIL:  Key (id)=(1) is still referenced from table "delete_regress_researcher_contacts".


----------------------------------------------------------------------
Ran 5 tests in 2.766s

FAILED (errors=1)
Destroying test database for alias 'default'...
Destroying test database for alias 'other'...

Changed 5 years ago by kmtracey

Test that demonstrates the problem, fails on trunk, passes on 1.2.X

comment:3 Changed 5 years ago by russellm

  • Triage Stage changed from Unreviewed to Accepted

comment:4 Changed 5 years ago by ramiro

Bisection shows this was introduced in r14507.

comment:5 Changed 5 years ago by russellm

  • Keywords blocker added

comment:6 Changed 5 years ago by alexkoshelev

  • Cc alexkoshelev added

comment:7 Changed 5 years ago by carljm

  • Owner changed from nobody to carljm
  • Status changed from reopened to new

comment:8 Changed 5 years ago by carljm

  • Resolution set to fixed
  • Status changed from new to closed

Fixed in [15248]. Thanks to tbrizzi for the report, and Karen, Alex, Russ, and Ramiro for confirmation and test case.

comment:9 Changed 4 years ago by jacob

  • milestone 1.3 deleted

Milestone 1.3 deleted

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