#36800 closed Bug (fixed)
Issue with ManyToManyField renaming
| Reported by: | Josik | Owned by: | Clifford Gama |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | 6.0 |
| Severity: | Release blocker | Keywords: | |
| Cc: | Ryan P Kilby | Triage Stage: | Ready for checkin |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Since Django 6.0, using Postgres 16 database, when a ManyToManyField is renamed, it cannot be access anymore.
Models before field renaming:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
class Author(models.Model):
name = models.CharField()
books = models.ManyToManyField(Book)
In Postgres database I have the following table for the m2m relation my_app_author_books.
I then renamed the m2m field "books" to "volumes":
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200)
class Author(models.Model):
name = models.CharField()
volumes = models.ManyToManyField(Book)
This generates the following migration:
# Generated by Django 6.0 on 2025-12-15 10:04
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('my_app', '0001_initial'),
]
operations = [
migrations.RenameField(
model_name='author',
old_name='books',
new_name='volumes',
),
]
In Postgres database I still have the following table for the m2m relation my_app_author_books. The table was not renamed to my_app_author_volumes which was the case in Django 5.2.
Maybe this is a new behaviour but when I get all instances of volumes, in shell_plus:
author_instance.volumes.all()
gives
ProgrammingError: relation "my_app_author_volumes" does not exist LINE 1: ...y_app_book"."title" FROM "my_app_book" INNER JOIN "my_app_au...
Migrations were played
python manage.py showmigrations gives:
my_app [X] 0001_initial [X] 0002_rename_books_author_volumes
Change History (13)
comment:1 by , 3 weeks ago
comment:2 by , 3 weeks ago
| Owner: | set to |
|---|---|
| Severity: | Normal → Release blocker |
| Status: | new → assigned |
| Triage Stage: | Unreviewed → Accepted |
Thanks for the report! I haven't bisected yet but I tested against both 5.2. and 6.0 and reproduced the issue in the latter.
comment:4 by , 3 weeks ago
I think I'm also having the same issue. I had a many to many field and a test fixture. With django 6 im unable to load the fixture: error is that the table does not exists. Difference in naming is the cause:
Naming of many to many table
- Dev Database (tables created by django 5) : base_vergunningproductgroep_producten (plural)
- Test database (tables created by django 6) : base_vergunningproductgroep_product
Code highlighting:
class VergunningProductGroep(TenantModel): vergunning = models.ForeignKey(Vergunning, on_delete=models.CASCADE, related_name='vergunningproducten') producten = models.ManyToManyField(Product, related_name='vergunningproductgroepen')
comment:5 by , 3 weeks ago
| Cc: | added |
|---|
comment:6 by , 3 weeks ago
| Has patch: | set |
|---|
comment:7 by , 3 weeks ago
| Cc: | removed |
|---|
Not sure why I was pinged here and on Discord if there was already a patch, it gave me the impression there was an urgent need for me to look at this.
The proposed solution looks right to me albeit we might want to adjust the comment about no field alteration needed to take place if the name change as that's no true for many-to-many fields and likely what made us miss this in the first place.
comment:9 by , 3 weeks ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
comment:10 by , 3 weeks ago
| Patch needs improvement: | set |
|---|---|
| Triage Stage: | Ready for checkin → Accepted |
An Oracle failure to check.
comment:11 by , 3 weeks ago
| Patch needs improvement: | unset |
|---|---|
| Triage Stage: | Accepted → Ready for checkin |
I saw this too. sqlmigrate in 5.2 outputs something like
With 6.0, the ALTER TABLE line is replaced by
-- (no-op).Adding this to the migration works around it:
migrations.RunSQL( sql='ALTER TABLE "my_app_author_books" RENAME TO "my_app_author_volumes";', reverse_sql='ALTER TABLE "my_app_author_volumes" RENAME TO "my_app_author_books";', ),