Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert data on AlterField django migration

I have a production database and need to keep safe the data. I want to change a Field in model and convert all data inside that database with this change.

Old field

class MyModel(models.Model):
    field_name = models.TimeField()

Changed field

class MyModel(models.Model):
    field_name = models.PositiveIntegerField()

Basically I want to convert the TimeField value (that has a Time object) in minutes.

Example: I have in an object time(hour=2, minute=0, second=0) and I want to convert that field value in all database table to 120 when I apply the migrate.

like image 734
Rodolpho Pivetta Sabino Avatar asked Sep 22 '16 00:09

Rodolpho Pivetta Sabino


People also ask

What do Django migrations do to databases?

Migrations are Django's way of propagating changes you make to your models (adding a field, deleting a model, etc.) into your database schema. They're designed to be mostly automatic, but you'll need to know when to make migrations, when to run them, and the common problems you might run into.

How do I rollback migration in Django?

By doing python manage.py migrate app B (or A; both works) . Remove the migration files A and Y for time being from the project location. Now unapply B, by doing python manage.py migrate app X .

How do I move an existing database in Django?

To recap, the basic steps to use Django migrations look like this: Create or update a model. Run ./manage.py makemigrations <app_name> Run ./manage.py migrate to migrate everything or ./manage.py migrate <app_name> to migrate an individual app.

What is the difference between Makemigrations and migrate in Django?

So the difference between makemigrations and migrate is this: makemigrations auto generates migration files containing changes that need to be applied to the database, but doesn't actually change anyhting in your database. migrate will make the actual modifications to your database, based on the migration files.


2 Answers

In the past, I've always done this with the help of Djangos RunPython operation. Create a custom migration that handles the following.

  1. Change name of field.
  2. Add new field of desired type.
  3. RunPython to handle the logic of converting from one to the other.
  4. Delete old field.


def migrate_time_to_positive_int(apps, schema_editor):

    MyModel = apps.get_model('myapp', 'MyModel')

    for mm in MyModel.objects.all():

        field_old_time = mm.field_name_old
        field_new_int = field_old_time.total_seconds() / 60
        mm.field_name = field_new_int 
        mm.save()

class Migration(migrations.Migration):

    operations = [
        migrations.RenameField(
            model_name='mymodel',
            old_name='field_name',
            new_name='field_name_old',
        ),
        migrations.AddField(
            model_name='mymodel',
            name='field_name',
            field=models.PositiveIntegerField(),
        ),
        migrations.RunPython(migrate_time_to_positive_int),
        migrations.RemoveField(
            model_name='mymodel',
            name='field_name_old',
        )
    ]

field_name_old.total_seconds() / 60 might need to be adjusted, but you get the idea.

like image 65
BottleZero Avatar answered Sep 17 '22 14:09

BottleZero


Safest way that I always do is:

  1. create another field with field_name_new = models.PositiveIntegerField()
  2. migrate this new field on production db
  3. do data migration (convert field_name value and move the converted value to field field_name_new)
  4. change your code so that field_name_new is used
  5. deploy the step 4
  6. delete the field field_name
  7. migrate the step 6 on production db and deploy it (the step 6)

there is only one bad side: you may loose some data in steps 4 and 5. but you can replicate these data to new field basically

like image 25
doniyor Avatar answered Sep 20 '22 14:09

doniyor