[Django]-What does RunPython.noop() do?

34👍

Sometimes it is possible that you want to revert a migration. For example you have added a field, but now you want to bring the database back to the state before the migration. You can do this by reverting the migration [Django-doc] with the command:

python3 manage.py migrate app_name previous_migration_name

Then Django will look how it can migrate back the the previous_migration_name and perform the operations necessary. For example if you renamed a field from foo to bar, then Django will rename it from bar back to foo.

Other operations are not reversible. For example if you remove a field in the migration, and that field has no default and is non-NULLable, then this can not be reversed. This makes sense since the reverse of removing a field is adding a field, but since there is no value to take for the existing records, what should Django fill in for that field that is recreated for the existing records?

A RunPython command is by default not reversible. In general in computer science one can not computationally determine the reverse of a function if any exists. This is a consequence of Rice’s theorem [wiki]. But sometimes it is possible. If we for example constructed a migration where we incremented a certain field with one, then the reverse is to decrement all the fields with one, for example:

from django.db.models import F
from django.db import migrations

def forwards_func(apps, schema_editor):
    MyModel = apps.get_model('my_app', 'MyModel')
    db_alias = schema_editor.connection.alias
    MyModel.objects.using(db_alias).all().update(
        some_field=F('some_field')+1
    ])

def reverse_func(apps, schema_editor):
    MyModel = apps.get_model('my_app', 'MyModel')
    db_alias = schema_editor.connection.alias
    MyModel.objects.using(db_alias).all().update(
        some_field=F('some_field')-1
    ])

class Migration(migrations.Migration):

    dependencies = []

    operations = [
        migrations.RunPython(code=forwards_func, reverse_code=reverse_func),
    ]

But sometimes it is possible that a (data)migration does nothing when you migrate it forward, or more common when you migrate it backwards. Instead of each time implementing an empty function, you can then pass a reference to noop, which does nothing:

from django.db.models import F
from django.db import migrations

def forwards_func(apps, schema_editor):
    # … some action …
    pass

class Migration(migrations.Migration):

    dependencies = []

    operations = [
        migrations.RunPython(code=forwards_func, reverse_code=migrations.RunPython.noop),
    ]

Leave a comment