Search code examples
python-3.xdjangopostgresqlpsycopg2

Django flush raises "cannot truncate a table referenced in a foreign key constraint"


Consider the following models.py:

from django.db import models

class Foo(models.Model):
    bars = models.ManyToManyField(
        "Bar",
        blank=True
    )

class Bar(models.Model):
    foos = models.ManyToManyField(
        "Foo",
        blank=True,
        through="Foo_bars"
    )

and the associated migration:

from django.db import migrations, models


class Migration(migrations.Migration):
    initial = True

    dependencies = []

    operations = [
        migrations.CreateModel(
            name="Bar",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
            ],
        ),
        migrations.CreateModel(
            name="Foo",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                ("bars", models.ManyToManyField(blank=True, to="demo.bar")),
            ],
        ),
        migrations.AddField(
            model_name="bar",
            name="foos",
            field=models.ManyToManyField(blank=True, to="demo.foo"),
        ),
    ]

Running

python manage.py migrate && python manage.py flush --no-input --traceback

produces the following trace back

  File "/opt/venv/lib/python3.13/site-packages/psycopg/cursor.py", line 97, in execute
    raise ex.with_traceback(None)
django.db.utils.NotSupportedError: cannot truncate a table referenced in a foreign key constraint
DETAIL:  Table "demo_bar_foos" references "demo_foo".
HINT:  Truncate table "demo_bar_foos" at the same time, or use TRUNCATE ... CASCADE.

How can I fix this problem? How can I add cascade?

A full reproducible example is available at https://github.com/rgaiacs/django-flush-problem.


Solution

  • This is not how you define ManyToManyFields. If you define a ManyToManyField in one of the models, you can access it in the other direction because of the foo_bars, so:

    from django.db import models
    
    
    class Foo(models.Model):
        bars = models.ManyToManyField('Bar', blank=True, related_name='foos')
    
    
    class Bar(models.Model):
        # foos = models.ManyToManyField(
        #    "Foo",
        #    blank=True,
        #    through="Foo_bars"
        # )
        pass

    It thus allows to work with my_bar.foos.all() because of the related_name=... [Django-doc].