Django Migrations Add Field with Default as Function of Model
I just learnt how to do this with a single migration!
when running makemigrations
django should ask you to set a one-off default. Define whatever you can here to keep it happy, and you'll end up with the migration AddField
you mentioned.
migrations.AddField( model_name='series', name='updated_as', field=models.DateTimeField(default=????, auto_now=True),),
Change this one operation into 3 operations:
- initially make the field nullable, so the column will be added.
- call a function to populate the field as needed.
- alter the field (with
AlterField
) to make it not nullable (like the above, with no default).
so you end up with something like.
migrations.AddField( model_name='series', name='updated_as', field=models.DateTimeField(null=True, auto_now=True),),migrations.RunPython(set_my_defaults, reverse_func),migrations.AlterField( model_name='series', name='updated_as', field=models.DateTimeField(auto_now=True),),
with your functions defined as something like:
def set_my_defaults(apps, schema_editor): Series = apps.get_model('myapp', 'Series') for series in Series.objects.all().iterator(): series.updated_as = datetime.now() + timedelta(days=series.some_other_field) series.save()def reverse_func(apps, schema_editor): pass # code for reverting migration, if any
Except, you know, not terrible; consider using F expressions and/or database functions to increase migration performance for large databases.
You need to do it in two migrations. First of all, add your field, but make nullable. Create a migration file as usual. After that set your field to not-nullable and run makemigrations again, but don't lauch migrate yet. Open the second migration and define a function at the top:
def set_field_values(apps, schema_editor): # use apps.get_model("app_name", "model_name") and set the defualt values
then, in your migration file there is a list of operations. Before the alter field operation add
RunPython(set_field_values)
and it should do it
You should also define a reverse for your function set_my_defaults()
, in case you what to revert the migration in the future.
def reverse_set_default(apps, schema_editor): pass
The reverse function in this case need to do nothing, since you are removing the field.
And add it to RunPython:
migrations.RunPython(set_my_defaults, reverse_set_default),