[Django] #36839: Renaming of content types on RenameModel operations silently passes when duplicates are found

12 views
Skip to first unread message

Django

unread,
Dec 31, 2025, 3:42:07 PM12/31/25
to django-...@googlegroups.com
#36839: Renaming of content types on RenameModel operations silently passes when
duplicates are found
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Type: Bug
Status: new | Component:
| contrib.contenttypes
Version: | Severity: Normal
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
When renaming a model, the `migrate` command will inject operations to
rename content types. When a target content type already exists, the
operation just silently skips the update:
{{{#!py
content_type.model = new_model
try:
with transaction.atomic(using=db):
content_type.save(using=db, update_fields={"model"})
except IntegrityError:
# Gracefully fallback if a stale content type causes a
# conflict as remove_stale_contenttypes will take care of
# asking the user what should be done next.
content_type.model = old_model
}}}

The rationale for skipping was discussed
[https://github.com/django/django/pull/6612#issuecomment-219584224 here]:

> In this case I think the safe approach is simply to silently handle the
integrity error and let the update_contenttypes's post_migrate handler
prompt the user about what to do next.

But that `post_migrate` handler was later removed in
6a2af01452966d10155b720f4f5e7b09c7e3e419 in favor of a management command
that can be run on demand, meaning the pass is now silent again, AFAICT.

I don't think the silent pass is desired. I can add more details if this
isn't enough context, but I think it's worth an initial investigation.
--
Ticket URL: <https://code.djangoproject.com/ticket/36839>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Jan 1, 2026, 7:23:27 PMJan 1
to django-...@googlegroups.com
#36839: Renaming of content types on RenameModel operations silently passes when
duplicates are found
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: (none)
Type: Bug | Status: new
Component: | Version:
contrib.contenttypes |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by mag123c):

I looked into this issue and traced the behavior in
`django/contrib/contenttypes/management/__init__.py`.

When `IntegrityError` is caught during the rename (lines 29-33), the code
silently reverts to the old model name. The comment mentions
`remove_stale_contenttypes` will handle it, but as noted, this is now an
optional management command - users have no way of knowing the rename
failed.

Possible approaches:
1. Emit a warning via `warnings.warn()` when the conflict occurs
2. Log at WARNING level so it appears in server logs
3. Raise an exception (though this may be too disruptive)

I'm curious whether the silent behavior was intentional for backwards
compatibility, or if surfacing this to users would be welcome.
--
Ticket URL: <https://code.djangoproject.com/ticket/36839#comment:1>

Django

unread,
Jan 1, 2026, 8:11:18 PMJan 1
to django-...@googlegroups.com
#36839: Renaming of content types on RenameModel operations silently passes when
duplicates are found
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: (none)
Type: Bug | Status: new
Component: | Version:
contrib.contenttypes |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by mag123c):

* cc: mag123c (added)

--
Ticket URL: <https://code.djangoproject.com/ticket/36839#comment:2>

Django

unread,
Jan 2, 2026, 3:03:43 PMJan 2
to django-...@googlegroups.com
#36839: Renaming of content types on RenameModel operations silently passes when
duplicates are found
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Moksha
| Choksi
Type: Bug | Status: assigned
Component: | Version:
contrib.contenttypes |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Moksha Choksi):

* owner: (none) => Moksha Choksi
* status: new => assigned

--
Ticket URL: <https://code.djangoproject.com/ticket/36839#comment:3>

Django

unread,
Jan 7, 2026, 5:09:48 AMJan 7
to django-...@googlegroups.com
#36839: Renaming of content types on RenameModel operations silently passes when
duplicates are found
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Moksha
| Choksi
Type: Bug | Status: assigned
Component: | Version:
contrib.contenttypes |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Moksha Choksi):

* has_patch: 0 => 1

--
Ticket URL: <https://code.djangoproject.com/ticket/36839#comment:4>

Django

unread,
Jan 14, 2026, 11:48:12 AM (10 days ago) Jan 14
to django-...@googlegroups.com
#36839: Renaming of content types on RenameModel operations silently passes when
duplicates are found
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Moksha
| Choksi
Type: Bug | Status: closed
Component: | Version: 6.0
contrib.contenttypes |
Severity: Normal | Resolution: needsinfo
Keywords: | Triage Stage:
| Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* resolution: => needsinfo
* status: assigned => closed
* version: => 6.0

Comment:

Hello Jacob, thank you for your ticket! I have spent a while trying to
reproduce and somehow I have made a mess of my content types without being
able to reproduce exactly this. Could you please provide instructions to
reproduce? Thank you!
--
Ticket URL: <https://code.djangoproject.com/ticket/36839#comment:5>

Django

unread,
Jan 14, 2026, 1:03:28 PM (10 days ago) Jan 14
to django-...@googlegroups.com
#36839: Renaming of content types on RenameModel operations silently passes when
duplicates are found
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Moksha
| Choksi
Type: Bug | Status: closed
Component: | Version: 6.0
contrib.contenttypes |
Severity: Normal | Resolution: needsinfo
Keywords: | Triage Stage:
| Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Comment (by Jacob Walls):

Sure thing. With a fresh app:

1. add models:

{{{#!py
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models


class Song(models.Model):
melodious = models.BooleanField(default=True)


class Melody(models.Model):
melodious = models.BooleanField(default=True)


class TaggedSong(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType,
on_delete=models.CASCADE)
object_id = models.PositiveBigIntegerField()
content_object = GenericForeignKey("content_type", "object_id")
}}}

2. `makemigrations` && `migrate`
3. In a shell:
{{{#!py
>>> song = Song.objects.create()
>>> tagged = TaggedSong(tag="hit", content_object=song)
>>> tagged.save()
}}}
4. Delete the melody class, makemigrations, migrate. You now have a stale
content type for the model "melody". Before, there used to be a
`post_migrate` handler alerting you to the situation.
5. Rename `Song` to `Melody`, makemigrations, migrate, answer "Renamed?"
with Y.
6. If you instrumented the `except IntegrityError` mentioned in the ticket
body, you hit that silent pass.
7. You can't look up your tagged items by their new model name, because
they were silently ignored:

{{{#!py
>>> TaggedSong.objects.filter(content_type__model="melody")
<QuerySet []>
>>> TaggedSong.objects.filter(content_type__model="song")
<QuerySet [<TaggedSong: TaggedSong object (1)>]>
}}}

If I were a little more fluent with content types and generic foreign keys
I could probably construct a scenario that would show lookups resulting in
`ObjectDoesNotExist`, the point is that the data didn't migrate. The
content type is now fresh:

{{{#!py
>>> ContentType.objects.get_for_model(Melody)
<ContentType: Myapp | melody>
}}}
... but all your data is on the old one (see step 7.)
--
Ticket URL: <https://code.djangoproject.com/ticket/36839#comment:6>

Django

unread,
Jan 14, 2026, 1:19:52 PM (10 days ago) Jan 14
to django-...@googlegroups.com
#36839: Renaming of content types on RenameModel operations silently passes when
duplicates are found
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Moksha
| Choksi
Type: Bug | Status: new
Component: | Version: 6.0
contrib.contenttypes |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Jacob Walls):

* resolution: needsinfo =>
* status: closed => new

--
Ticket URL: <https://code.djangoproject.com/ticket/36839#comment:7>

Django

unread,
Jan 14, 2026, 8:46:06 PM (10 days ago) Jan 14
to django-...@googlegroups.com
#36839: Renaming of content types on RenameModel operations silently passes when
duplicates are found
-------------------------------------+-------------------------------------
Reporter: Jacob Walls | Owner: Moksha
| Choksi
Type: Bug | Status: new
Component: | Version: 6.0
contrib.contenttypes |
Severity: Normal | Resolution:
Keywords: | Triage Stage: Accepted
Has patch: 1 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Natalia Bidart):

* stage: Unreviewed => Accepted

Comment:

Thank you Jacob, with these instructions I was able to reproduce. Not only
the data did not migrate, but the old content type was left behind:
{{{#!sql
django=# SELECT * FROM django_content_type WHERE app_label =
'ticket_36839';
id | app_label | model
----+--------------+------------
65 | ticket_36839 | melody
66 | ticket_36839 | song
67 | ticket_36839 | taggedsong
(3 rows)

django=# SELECT * FROM ticket_36839_taggedsong;
id | tag | object_id | content_type_id
----+-----+-----------+-----------------
1 | hit | 1 | 66
(1 row)
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/36839#comment:8>
Reply all
Reply to author
Forward
0 new messages