#24630 closed Bug (fixed)
Misleading/incorrect docs about RunPython and transactions
Reported by: | Luca Corti | Owned by: | priidukull |
---|---|---|---|
Component: | Documentation | Version: | 1.8 |
Severity: | Normal | Keywords: | uuidfiled migrations |
Cc: | priidukull | 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 (last modified by )
As per #23932, I added an UUIDField to a model following the documentation at https://docs.djangoproject.com/en/1.8/howto/writing-migrations/. The database is PostgreSQL.
When running the migration I get this error:
File "manage.py", line 10, in <module> execute_from_command_line(sys.argv) File "venv/lib/python2.7/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line utility.execute() File "venv/lib/python2.7/site-packages/django/core/management/__init__.py", line 330, in execute self.fetch_command(subcommand).run_from_argv(self.argv) File "venv/lib/python2.7/site-packages/django/core/management/base.py", line 390, in run_from_argv self.execute(*args, **cmd_options) File "venv/lib/python2.7/site-packages/django/core/management/base.py", line 441, in execute output = self.handle(*args, **options) File "venv/lib/python2.7/site-packages/django/core/management/commands/migrate.py", line 221, in handle executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial) File "venv/lib/python2.7/site-packages/django/db/migrations/executor.py", line 110, in migrate self.apply_migration(states[migration], migration, fake=fake, fake_initial=fake_initial) File "venv/lib/python2.7/site-packages/django/db/migrations/executor.py", line 147, in apply_migration state = migration.apply(state, schema_editor) File "venv/lib/python2.7/site-packages/django/db/migrations/migration.py", line 115, in apply operation.database_forwards(self.app_label, schema_editor, old_state, project_state) File "venv/lib/python2.7/site-packages/django/db/migrations/operations/fields.py", line 201, in database_forwards schema_editor.alter_field(from_model, from_field, to_field) File "venv/lib/python2.7/site-packages/django/db/backends/base/schema.py", line 484, in alter_field old_db_params, new_db_params, strict) File "venv/lib/python2.7/site-packages/django/db/backends/base/schema.py", line 637, in _alter_field params, File "venv/lib/python2.7/site-packages/django/db/backends/base/schema.py", line 107, in execute cursor.execute(sql, params) File "venv/lib/python2.7/site-packages/django/db/backends/utils.py", line 79, in execute return super(CursorDebugWrapper, self).execute(sql, params) File "venv/lib/python2.7/site-packages/django/db/backends/utils.py", line 64, in execute return self.cursor.execute(sql, params) File "venv/lib/python2.7/site-packages/django/db/utils.py", line 97, in __exit__ six.reraise(dj_exc_type, dj_exc_value, traceback) File "venv/lib/python2.7/site-packages/django/db/backends/utils.py", line 64, in execute return self.cursor.execute(sql, params) django.db.utils.OperationalError: cannot ALTER TABLE "mytable" because it has pending trigger events
Change History (15)
comment:1 by , 10 years ago
Description: | modified (diff) |
---|
comment:4 by , 10 years ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
comment:5 by , 10 years ago
I tried to reproduce the bug (as explained by KevinEtienne) with Python 2.7 and Django 1.7/1.8. Did not manage to reproduce the issue. I used the following migrations.
0001_initial.py:
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ ] operations = [ migrations.CreateModel( name='Choice', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('choice_text', models.CharField(max_length=200)), ('votes', models.IntegerField(default=0)), ], ), migrations.CreateModel( name='Question', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('question_text', models.CharField(max_length=200)), ('pub_date', models.DateTimeField(verbose_name=b'date published')), ], ), migrations.AddField( model_name='choice', name='question', field=models.ForeignKey(to='polls.Question'), ), ]
0002_second.py:
# -*- coding: utf-8 -*- from __future__ import unicode_literals import random from django.db import migrations def populate_old_field(apps, schema_editor): Question = apps.get_model('polls', 'Question') for i in range(1, 100): q = Question(i, random.getrandbits(128), '2015-09-09') q.save() class Migration(migrations.Migration): dependencies = [ ('polls', '0001_initial'), ] operations = [ migrations.RunPython( populate_old_field ), ]
0003_third.py:
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations def populate_new_field(apps, schema_editor): Question = apps.get_model('polls', 'Question') for q in Question.objects.all(): q.question_text_new = q.question_text q.save() class Migration(migrations.Migration): dependencies = [ ('polls', '0002_second'), ] operations = [ migrations.AddField( model_name='question', name='question_text_new', field=models.CharField(max_length=300, null=True), ), migrations.RunPython( populate_new_field, ), migrations.RemoveField( model_name='question', name='question_text', ), ]
comment:6 by , 10 years ago
Resolution: | → needsinfo |
---|---|
Status: | assigned → closed |
comment:7 by , 10 years ago
Cc: | added |
---|
comment:8 by , 10 years ago
Hi, I've stumbled across this issue as well.
I'm not getting the error with your example either priidukull, so I reduced the code I had to a small example that still exhibits the bug (in both django 1.7 and 1.8, but only in psql, not sqlite)
0001_initial.py :
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations from django.conf import settings class Migration(migrations.Migration): dependencies = [] operations = [ migrations.CreateModel( name='Parent', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ], ), migrations.CreateModel( name='Child', fields=[ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('boolfield', models.BooleanField(default=True)), ('parent', models.ForeignKey(to='example.Parent')), ], ), ]
0002_add_data.py:
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations def add_data(apps, schema_editor): Parent = apps.get_model("example", "Parent") p = Parent(1) p.save() Child = apps.get_model("example", "Child") c = Child(1, parent=p, boolfield=True) c.save() class Migration(migrations.Migration): dependencies = [ ('example', '0001_initial'), ] operations = [ migrations.RunPython( add_data ) ]
0003_change_field_type.py:
# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations def convert_field(apps, schema_editor): Child = apps.get_model("example", "Child") for c in Child.objects.all(): c.textchoicefield = 'true' if c.boolfield else 'false' c.save() class Migration(migrations.Migration): dependencies = [ ('example', '0002_add_data'), ] operations = [ migrations.AddField( model_name='child', name='textchoicefield', field=models.TextField(default=b'none', choices=[(b'true', b'True'), (b'false', b'False'), (b'none', b'None')]), preserve_default=True, ), migrations.RunPython(convert_field), migrations.RemoveField( model_name='child', name='boolfield', ), ]
If I move RemoveField out of the last migration and into its own, it all works fine.
comment:9 by , 10 years ago
Resolution: | needsinfo |
---|---|
Status: | closed → new |
comment:10 by , 10 years ago
Component: | Migrations → Documentation |
---|---|
Severity: | Normal → Release blocker |
Summary: | UUIDField migration → Misleading/incorrect docs about RunPython and transactions |
Triage Stage: | Unreviewed → Accepted |
It seems to me that the documentation that says, "By default, RunPython will run its contents inside a transaction" is misleading (added in 5a917cfef319df33ca30a3b27bd0b0533b8e63bb).
Each *migration* is run in its own transaction (if the database backend has can_rollback_ddl = True
) because we use a SchemaEditor context manager when applying each migration, which creates a transaction.
I'm not sure we can make any changes code-wise to support mixing schema changes and RunPython operations in the same migration, but we should correct the documentation for current version of Django anyway.
Docs to fix:
- The second example migration needs to be split into at least two migrations. https://docs.djangoproject.com/en/1.8/howto/writing-migrations/#migrations-that-add-unique-fields
- "By default, RunPython will run its contents..." https://docs.djangoproject.com/en/1.8/ref/migration-operations/#runpython
comment:12 by , 10 years ago
Severity: | Release blocker → Normal |
---|
comment:13 by , 10 years ago
Triage Stage: | Accepted → Ready for checkin |
---|
I've run into what I believe the same issue with Django 1.7.x. I have a model where I wanted to add and remove a new field.
The migration had 3 operations:
The stacktrace looks exactly the same as quote in the original description. Not sure if this is a behavior expected from Django or not. The documentation on 1.7 and 1.8 states that
RunPython
should run into its own transaction.