Opened 3 months ago

Closed 3 months ago

#35510 closed Bug (invalid)

migration on_delete=django.db.models.deletion.CASCADE does not create a ON DELETE CASCADE constraint

Reported by: Daniel Ahern Owned by: nobody
Component: Database layer (models, ORM) Version: 5.0
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Python 3.12.0 (using a conda environment)
Django 5.6
MariaDB/MySQL 8.0.30-0ubuntu0.22.04.1 (Ubuntu)

models.py:

from django.db import models

# class TableBoolean(models.Model):
#     field1 = models.BooleanField(default=False)

#     class Meta:
#         db_table = 'test_boolean'

class Parent(models.Model):
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name
    class Meta:
        db_table = 'test_parent'

class Child(models.Model):
    parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'test_child'

Then I do:

python manage.py makemigrations v506

which makes:

# Generated by Django 5.0.6 on 2024-06-08 14:09

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='Parent',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=100)),
            ],
            options={
                'db_table': 'test_parent',
            },
        ),
        migrations.CreateModel(
            name='Child',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=100)),
                ('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='v506.parent')),
            ],
            options={
                'db_table': 'test_child',
            },
        ),
    ]

Running the migration creates a table that does NOT cascade delete:

| test_child | CREATE TABLE `test_child` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `parent_id` bigint NOT NULL,
  PRIMARY KEY (`id`),
  KEY `test_child_parent_id_e1a9888d_fk_test_parent_id` (`parent_id`),
  CONSTRAINT `test_child_parent_id_e1a9888d_fk_test_parent_id` FOREIGN KEY (`parent_id`) REFERENCES `test_parent` (`id`)
)

Then I test by:

mysql> insert into test_parent(name) values ('test_parent');

mysql> insert into test_child (name, parent_id) values ('test_child', 1);

mysql> delete from test_parent where id = 1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`django506`.`test_child`, CONSTRAINT `test_child_parent_id_e1a9888d_fk_test_parent_id` FOREIGN KEY (`parent_id`) REFERENCES `test_parent` (`id`))


I am able to alter the table:

ALTER TABLE `test_child`
DROP FOREIGN KEY `test_child_parent_id_e1a9888d_fk_test_parent_id`;

ALTER TABLE `test_child`
ADD CONSTRAINT `test_child_parent_id_e1a9888d_fk_test_parent_id`
FOREIGN KEY (`parent_id`) REFERENCES `test_parent` (`id`) ON DELETE CASCADE;

mysql> show create table test_child;

test_child | CREATE TABLE `test_child` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `parent_id` bigint NOT NULL,
  PRIMARY KEY (`id`),
  KEY `test_child_parent_id_e1a9888d_fk_test_parent_id` (`parent_id`),
  CONSTRAINT `test_child_parent_id_e1a9888d_fk_test_parent_id` FOREIGN KEY (`parent_id`) REFERENCES `test_parent` (`id`) ON DELETE CASCADE

mysql> delete from test_parent where id = 1;
Query OK, 1 row affected (0.01 sec)

mysql> select count(*) from test_child;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

I would expect ON DELETE CASCADE to be on the table made by the migration because the migration has "on_delete=django.db.models.deletion.CASCADE" on the line:

('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='v506.parent')),

Change History (1)

comment:1 by Tim Graham, 3 months ago

Resolution: invalid
Status: newclosed

As documented: When an object referenced by a ForeignKey is deleted, Django will emulate the behavior of the SQL constraint specified by the on_delete argument. ... on_delete doesn’t create an SQL constraint in the database. Support for database-level cascade options may be implemented later.

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