Opened 7 years ago

Closed 7 years ago

Last modified 4 years ago

#27585 closed Bug (invalid)

ManyToMany relationship is cascading delete after being detached

Reported by: Teresa Owned by: nobody
Component: Database layer (models, ORM) Version: 1.8
Severity: Normal Keywords: ManyToMany, cascade, delete, remove
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Given two models A and B that have a many-to-many relationship, removing all of the As from a model B, then deleting B results in all of the As that should now be detached from B being deleted. This is happening inside of a single transaction, using an SQLite database backend, as well as a MySQL backend.

Below is code that will reproduce the issue:

class A(models.Model):
  bs = models.ManyToManyField(B, related_name='as')

class B(models.Model):
  ...

def delete_b_without_deleting_as(b):
  with transaction.atomic():
    to_be_orphaned_as = [a for a in b.as.all()]
    for a in to_be_orphaned_as:
      b.as.remove(a)
    b.delete()

This results in both 'b' and all of the 'a's that had been a part of b.as.all() to be deleted from the database.

The expected behaviour would be for b.delete() to not cascade as it is a ManyToMany field, and in addition all of the 'a's have been detached from the relationship with 'b'. There is no reason for deleting 'b' to cascade and delete models that no longer have any relationship with 'b'.

Change History (6)

comment:1 by Teresa, 7 years ago

Resolution: invalid
Status: newclosed

comment:2 by Teresa, 7 years ago

Resolution: invalid
Status: closednew

An additional detail:

The models also have a ManyToOne foreign key constraint. But when that relationship has also been removed, the now unattached 'a's are deleted.

comment:3 by Tim Graham, 7 years ago

Could you please provide a sample app with models and tests we could download to reproduce? The example code doesn't run as b.as.all() is a SyntaxError.

comment:4 by Teresa, 7 years ago

Resolution: invalid
Status: newclosed

comment:5 by Matt, 4 years ago

This problem just happened to me on Django 2.2.12 in a migration. e.g.

Category = apps.get_model('blog', 'Category')

cat = Category.objects.filter(name='Cat A').first()

for article in list(cat.article_set.all()):
    cat.article_set.remove(article)

# This deleted all articles that were in the category but were already removed()
cat.delete()

comment:6 by Simon Charette, 4 years ago

Matt, could you provide a full set of models to reproduce.

I cannot reproduce on Django 2.2 and cascade deletion should not propagate this way through many-to-many relationships.

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