#34058 closed Bug (fixed)
Widening AutoField to BigAutoField, fails to widen the sequence.
Reported by: | Anders Kaseorg | Owned by: | Mariusz Felisiak |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | 4.1 |
Severity: | Release blocker | Keywords: | |
Cc: | Florian Apolloner, Mariusz Felisiak | Triage Stage: | Accepted |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
#30511 changed the behavior of migrations that widen AutoField
, removing the code that updates the type of the corresponding sequence. This creates a problem in projects that were upgraded from Django 4.0 or earlier, where the AutoField
s will still be serial
columns, and then migrated in Django 4.1, where the sequence will not be updated automatically.
Here’s a self-contained reproduction recipe:
- Install an old version of Django:
$ pip install django==4.0.7 psycopg2-binary
- Create a minimal test project:
# manage.py import os, sys from django.core.management import execute_from_command_line os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_settings") execute_from_command_line(sys.argv)
# my_settings.py DATABASES = { "default": { "ENGINE": "django.db.backends.postgresql", "NAME": "my_database", } } INSTALLED_APPS = ["my_app"]
# my_app/models.py from django.db import models class Widget(models.Model): id = models.SmallAutoField(primary_key=True)
- Create the PostgreSQL database and run migrations:
$ createdb my_database $ python manage.py makemigrations my_app Migrations for 'my_app': my_app/migrations/0001_initial.py - Create model Widget $ python manage.py migrate Operations to perform: Apply all migrations: my_app Running migrations: Applying my_app.0001_initial... OK
- Upgrade to Django 4.1:
$ pip install Django==4.1
- Edit
my_app/models.py
to widen the field fromSmallAutoField
toBigAutoField
:# my_app/models.py from django.db import models class Widget(models.Model): id = models.BigAutoField(primary_key=True)
- Run migrations:
$ ./manage.py makemigrations Migrations for 'my_app': my_app/migrations/0002_alter_widget_id.py - Alter field id on widget $ ./manage.py migrate Operations to perform: Apply all migrations: my_app Running migrations: Applying my_app.0002_alter_widget_id... OK
- Observe that the field has been widened but the sequence corresponding to it has not:
$ ./manage.py dbshell psql (14.5, server 13.8) Type "help" for help. my_database=> \d my_app_widget Table "public.my_app_widget" Column | Type | Collation | Nullable | Default --------+--------+-----------+----------+------------------------------------------- id | bigint | | not null | nextval('my_app_widget_id_seq'::regclass) Indexes: "my_app_widget_pkey" PRIMARY KEY, btree (id) my_database=> \d my_app_widget_id_seq Sequence "public.my_app_widget_id_seq" Type | Start | Minimum | Maximum | Increment | Cycles? | Cache ----------+-------+---------+---------+-----------+---------+------- smallint | 1 | 1 | 32767 | 1 | no | 1 Owned by: public.my_app_widget.id
- Try and fail to create a large number of rows:
$ ./manage.py shell Python 3.10.6 (main, Aug 1 2022, 20:38:21) [GCC 11.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from my_app.models import Widget >>> Widget.objects.bulk_create(Widget() for i in range(40000)) Traceback (most recent call last): File "…/lib/python3.10/site-packages/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) psycopg2.errors.SequenceGeneratorLimitExceeded: nextval: reached maximum value of sequence "my_app_widget_id_seq" (32767) The above exception was the direct cause of the following exception: Traceback (most recent call last): File "<console>", line 1, in <module> … File "…/lib/python3.10/site-packages/django/db/backends/utils.py", line 89, in _execute return self.cursor.execute(sql, params) django.db.utils.DataError: nextval: reached maximum value of sequence "my_app_widget_id_seq" (32767)
Change History (10)
comment:1 by , 2 years ago
Severity: | Normal → Release blocker |
---|---|
Summary: | Upgrading to Django 4.1, then widening AutoField to BigAutoField, fails to widen the corresponding sequence → Widening AutoField to BigAutoField, fails to widen the sequence. |
Triage Stage: | Unreviewed → Accepted |
comment:2 by , 2 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:4 by , 2 years ago
Needs documentation: | unset |
---|---|
Needs tests: | unset |
follow-up: 8 comment:7 by , 2 years ago
I found a scenario in which this fix doesn’t work: if the field had been renamed before the upgrade, the corresponding sequence will not have been renamed.
To modify my above reproduction recipe, insert a new step between steps 3 and 4 renaming the field:
# my_app/models.py from django.db import models class Widget(models.Model): renamed_id = models.SmallAutoField(primary_key=True)
$ ./manage.py makemigrations Was widget.id renamed to widget.renamed_id (a SmallAutoField)? [y/N] y Migrations for 'my_app': my_app/migrations/0002_rename_id_widget_renamed_id.py - Rename field id on widget to renamed_id $ ./manage.py migrate Operations to perform: Apply all migrations: my_app Running migrations: Applying my_app.0002_rename_id_widget_renamed_id... OK
(and then keep the new name in step 5).
comment:8 by , 2 years ago
Replying to Anders Kaseorg:
I found a scenario in which this fix doesn’t work: if the field had been renamed before the upgrade, the corresponding sequence will not have been renamed.
To modify my above reproduction recipe, insert a new step between steps 3 and 4 renaming the field:
# my_app/models.py from django.db import models class Widget(models.Model): renamed_id = models.SmallAutoField(primary_key=True)$ ./manage.py makemigrations Was widget.id renamed to widget.renamed_id (a SmallAutoField)? [y/N] y Migrations for 'my_app': my_app/migrations/0002_rename_id_widget_renamed_id.py - Rename field id on widget to renamed_id $ ./manage.py migrate Operations to perform: Apply all migrations: my_app Running migrations: Applying my_app.0002_rename_id_widget_renamed_id... OK(and then keep the new name in step 5).
Good catch! It's partly a long-standing issue, however, IMO, we should backport the fix.
Thanks for the report! Regression in 2eea361eff58dd98c409c5227064b901f41bd0d6.