Home > Software engineering >  How to add new field in model after custom migration which access model before new field?
How to add new field in model after custom migration which access model before new field?

Time:10-24

I have custom migration file which creates entry in table user. Initial working project has these migrations:

 1. 001_initial.py # creates model with some fields
 2. 002_custom_user_data.py # adds data in "user" due some middle requirement after some time.

Now I want to add one more column to this table as "config_data". For this I have followed below steps:

 1. python manage.py makemigrations <app-name> # creates one more migration file` 003_add_config_data_field.py
 2. python manage.py migrate # raise an error

migrate command raises below error.

      Applying my_user.001_initial.py... OK
      Applying my_user.002_custom_user_data.py ...Traceback (most recent call last):
  File "/home/myhome/.local/share/virtualenvs/myproject/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedColumn: column my_user.config_data does not exist
LINE 1: ..., "my_user"."address",

This is because migration 002 tries to access my_user table before the add field migration(003) is migrated in the table, which is raising UndefinedColumn config_data error.

Any idea how can be this resolved as these migrations should work for fresh DB as well as production dB in which till 002 is migrated earlier.

Below is the migration for 002_custom_user_data.py

# Generated by Django 3.2.2

from django.db import migrations
from myapp.models import MyUser


def create_user(apps, schema_editor):
    user = MyUser.objects.get_or_create(id=123)
    user.address = 'demo'
    user.save()


def delete_user(apps, schema_editor):
    MyUser.objects.filter(id=123).delete()


class Migration(migrations.Migration):

    dependencies = [
        ('my_user', '001_initial'),
    ]
    operations = [
    migrations.RunPython(create_user, delete_user, elidable=True)
]

Thanks.

CodePudding user response:

You should import the historical model, not your current model. Django keeps track how the model looks like before you run that migration, you can access such model with the apps.get_model(…) method [Django-doc]:

from django.db import migrations
# No import from myapp.models!


def create_user(apps, schema_editor):
    MyUser = apps.get_model('my_user', 'MyUser')
    user = MyUser.objects.get_or_create(id=123)
    user.address = 'demo'
    user.save()


def delete_user(apps, schema_editor):
    MyUser = apps.get_model('my_user', 'MyUser')
    MyUser.objects.filter(id=123).delete()


class Migration(migrations.Migration):

    dependencies = [
        ('my_user', '001_initial'),
    ]

The MyUser model that we thus here use will not per se have all fields of the one you define in models.py, nor will the fields per se have the same type. You thus can here work with the model like it was at that point in time, and thus define data migrations. The records will than later be updated if you made an extra field for example.

  • Related