Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Migrations Add Field with Default as Function of Model

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,     ), 
like image 489
Alex Rothberg Avatar asked Apr 22 '15 04:04

Alex Rothberg


People also ask

How do you add a new field to a model with new Django migrations?

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.

What is difference between migrate and Makemigrations in Django?

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.

What does fake migration do in Django?

--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.


1 Answers

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:

  1. Initially make the field nullable, so the column will be added.
  2. Call a function to populate the field as needed.
  3. 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.

Note: Consider using F expressions and/or database functions to increase migration performance for large databases.

like image 57
FraggaMuffin Avatar answered Oct 25 '22 20:10

FraggaMuffin