Django porting using RunPython to commit changes

I want to change the foreign key in one of my models that currently cannot have nullable nulls.

I removed null=True from my field and ran makemigrations

Since I am modifying a table that already has rows that contain NULL values ​​in this field, I am prompted to immediately provide a one-time value or edit the migration file and add the RunPython operation.

My RunPython operation is displayed before the AlterField operation and performs the necessary update for this field, so it does not contain NULL values ​​(only rows that already contain NULL).

But the migration still fails with this error: django.db.utils.OperationalError: cannot ALTER TABLE "my_app_site" because it has pending trigger events

Here is my code:

 # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations def add_default_template(apps, schema_editor): Template = apps.get_model("my_app", "Template") Site = apps.get_model("my_app", "Site") accept_reject_template = Template.objects.get(name="Accept/Reject") Site.objects.filter(template=None).update(template=accept_reject_template) class Migration(migrations.Migration): dependencies = [ ('my_app', '0021_auto_20150210_1008'), ] operations = [ migrations.RunPython(add_default_template), migrations.AlterField( model_name='site', name='template', field=models.ForeignKey(to='my_app.Template'), preserve_default=False, ), ] 

If I understand correctly, this error can occur when the field is changed so that it is not null, but the field contains null values. In this case, the only reason I can understand why this is happening is because the transaction of the RunPython operation RunPython not β€œ RunPython ” the changes to the database before running AlterField .

If this is really the reason - how can I make sure the changes are reflected in the database? If not, what could be causing the error?

Thank!

+18
django django-migrations
Feb 10 '15 at 10:57
source share
2 answers

This is because Django creates restrictions as DEFERRABLE INITIALLY DEFERRED :

 ALTER TABLE my_app_site ADD CONSTRAINT "[constraint_name]" FOREIGN KEY (template_id) REFERENCES my_app_template(id) DEFERRABLE INITIALLY DEFERRED; 

This tells PostgreSQL that the foreign key does not need to be verified immediately after each command, but can be delayed until the end of the transaction.

So, when a transaction changes content and structure, constraints are checked in parallel with structure changes, or checks are planned after the structure change. Both of these conditions are bad, and the database will abort the transaction, and not make any assumptions.

You can instruct PostgreSQL to immediately check the constraints in the current transaction by calling SET CONSTRAINTS ALL IMMEDIATE , so changing the structure will not be a problem (see SET CONTRACTS ). Your migration should look like this:

 operations = [ migrations.RunSQL('SET CONSTRAINTS ALL IMMEDIATE', reverse_sql=migrations.RunSQL.noop), # ... the actual migration operations here ... migrations.RunSQL(migrations.RunSQL.noop, reverse_sql='SET CONSTRAINTS ALL IMMEDIATE'), ] 

The first operation is intended for the application (forwarding) of migrations, and the last - for unacceptable (reverse) migrations.

EDIT: Delay deferment is useful to prevent insertion sorting, especially for self-regulatory tables and circular dependency tables. Therefore, be careful when bending Django.

LAST EDIT: On Django 1.7 and later, there is a special SeparateDatabaseAndState operation that allows data changes and structural changes during the same migration. Try using this operation before moving on to the "set constraints all all" method above. Example:

 operations = [ migrations.SeparateDatabaseAndState(database_operations=[ # put your sql, python, whatever data migrations here ], state_operations=[ # field/model changes goes here ]), ] 
+26
Sep 16 '16 at 22:44
source share

Yes, I would say that these are transaction boundaries that prevent data migration in your migration before running ALTER.

I would do as @danielcorreia says and implement it as two migrations, since it looks like a SchemaEditor is transaction-linked through the context manager that you should use.

+17
Feb 10 '15 at 12:13
source share



All Articles