I added a new, non-nullable field to my Django model and am trying to use migrations to deploy that change. How would I set default value to use for existing models to be some function of those models rather than a constant?
As an example let's say I previously had a created_on
field and I just added an updated_on
field whose value I want to set initially to the model's created_on
. How would I do this in a migration?
This is what I am trying to start with:
migrations.AddField( model_name='series', name='updated_as', field=models.DateTimeField(default=????, auto_now=True), preserve_default=False, ),
To answer your question, with the new migration introduced in Django 1.7, in order to add a new field to a model you can simply add that field to your model and initialize migrations with ./manage.py makemigrations and then run ./manage.py migrate and the new field will be added to your DB.
makemigrations is responsible for packaging up your model changes into individual migration files - analogous to commits - and migrate is responsible for applying those to your database.
--fake-initialAllows Django to skip an app's initial migration if all database tables with the names of all models created by all CreateModel operations in that migration already exist. This option is intended for use when first running migrations against a database that preexisted the use of migrations.
I just learned 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), preserve_default=False, ),
Change this one operation into 3 operations:
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.
Note: Consider using F expressions and/or database functions to increase migration performance for large databases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With