팀에서 공동으로 사용 중인 django 프로젝트에 내가 작성한 모델을 DB에 추가해야했다. 그런데 migrations 파일들이 .gitignore에 해당하여 이전 마이그레이션파일이 없는 상태로 나의 모델을 DB에 추가하려고하니 python manage.py makemigrations my_app 을 실행하니 에러가 발생하였다. 이에 대한 원인과 해결한 방법에 대해 알아보자.
django 프로젝트에 이미 my_app이라는 앱에 article이라는 모델을 DB에 마이그레이션한 상태라고 가정하자.
1) article 모델을 DB에 마이그레이션할 때
python [mange.py](http://maange.py) makemigrations my_app
명령어를 통해 생성한 파일을0001_initail.py
라고하자.2) 프로젝트에 기능을 추가하면서 article 모델에 여러 수정이 일어났고 맨 마지막 마이그레이션 파일이
0045_some_coding.py
라고하자3) 이제 나는 article 모델을 foreignKey로 참조하는 comment라는 모델을 새로 만들어서 DB에 추가하고싶다.
0001_initial.py
부터0045_some_coding.py
까지의 마이그레이션 파일이 나에게는 없는 상황이다. 이때 내가 comment를 DB에 추가하기위해 comment 모델에 대한 마이그레이션 파일을0046_my_app_comment.py
라는이름으로 만들고python [manage.py](http://manage.py) makemigrations my_app
명령어를 실행하자 에러가 발생한다.
- 모델 구조
# my_app/models.py # 기존에 정의되어있던 article 모델 class Article(models.Model): title = models.CharField(max_length=250) content = models.TextField() # 이번에 작업한 comment 모델 class Comment(models.Model): content = models.TextField() article = models.ForeignKey(Article, on_delete=models.CASCADE)
- 내가 추가하고 싶었던 마이그레이션 파일
# my_app/migrations/0046_my_app_comment.py class Migration(migrations.Migration): dependencies = [ ('my_app', '0045_some_coding.py') ] operations = [ migrations.CreateModel( name='Comment', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('content', models.TextField()), ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='place.article')), ], ), ]
원인은 comment가 article 모델을 참조하고있지만 현재 나에게는 이전 마이그레이션 파일, 즉 DB에 대한 정보파일이 없기 때문에 article을 참조할 때 에러가 발생하는 것이다.
만약 외부모델을 참조하지않는 comment 내부필드만 존재했을 경우 makemigrations 에서 에러가 발생하지는 않았을 것이다. 다만 이 경우 마이그레이션파일이 만들어졌다고 하더라도 migrate 명령어를 실행할 경우 제대로 작동하지않을 확률이 매우 높다. 그 이유에 대해서도 같이 알아보자.
마이그레이션파일은 쉽게 말해 DB 테이블의 변화 과정을 나타낸다. 마이그레이션할 때마다 현재 수정한 DB 상태가 이전과 무엇이 달라졌는지를 마이그레이션 파일을 통해 알 수 있는 것이다. 따라서
0001_initail.py
부터0045_some_coding.py
까지의 기록을 모두 반영하면 현재 DB의 상태가 되는 것이다.
따라서 내가 작업하기전 DB에 대한 마이그레이션 파일을 만들어야 한다. 마이그레이션파일이 하나도 없는 상태에서
python manage.py makemigrations my_app
실행하면0001_initail.py
라는 파일이 만들어지고 여기에는 현재 모델에 대한 정보가 반영되어있다. 여기서 내가 작업하기 전의 마이그레이션 파일을 만들기위해 일단 comment에 대한 마이그레이션 정보를 삭제한다. 그러면 일단0001_initail.py
부터0045_some_coding.py
의 정보, 즉 현재 DB 상태에 대한 정보가 방금 만든0001_initail.py
에 모두 반영되어있다고 할 수 있다.
python manage.py makemigrations my_app
로 생성되는0001_initial.py
파일
: 현재 모델에 대한 정보가 모두 있다.class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='Article', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=250)), ('content', models.TextField()), ], ), migrations.CreateModel( name='Comment', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('content', models.TextField()), ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='place.article')), ], ), ]
- 현재 DB 상태에 대한 마이그레이션 파일
: 내가 작업했던 Comment 모델에 대한 정보만 빼주면된다.# my_app/migrations/0001_initial.py class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='Article', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=250)), ('content', models.TextField()), ], ), ]
이번에는 반대로 내가 작업한 내용에 대한 것만 마이그레이션 파일로 만들어보자. 아까
python manage.py makemigrations my_app
해서 만들어졌던 파일에서 반대로 내가 작성한 내용의 정보만 남기고 나머지를 삭제하고 이전 파일에 대한 참조 정보를 넣어주면 된다.. 그리고0001_initail.py
파일은 아까 만들었으니 이름은 대충0002_my_app_comment.py
라고 지어보자
- 내가 작업한 내용에 대한 마이그레이션 파일
# my_app/migrations/0002_my_app_comment.py class Migration(migrations.Migration): dependencies = [ ('my_app', '0001_initail.py') ] operations = [ migrations.CreateModel( name='Comment', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('content', models.TextField()), ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='place.article')), ], ), ]
현재 DB에 대한 정보도 마이그레이션 파일로 담았고 내가 작업했던 내용도 마이그레이션 파일로 만들었기 때문에때문에 바로
python [manage.py](http://manage.py) migrate my_app
명령어를 실행하면 DB에 적용될 것 같다! → 물론 잘 실행될 수 있지만 추후 문제가 생길 확률이 매우 높고, 극히 드물게 다음과 같이 변화가 없다고 할수도있다.난 분명히 새로운 마이그레이션 파일을 만들었는데 . 왜그런걸까?
DB에 가서 django_migrations 라는 테이블을 보자
이런 데이터들이 있다. 이게 뭘까?
우리가pyhon manage.py migrate my_app
명령어를 실행하면 django는 해당 마이그레이션 파일 내용을 DB에 적용시키고 현재까지의 마이그레이션파일의 이름을 django_migrations라는 테이블에 저장해놓는다. 그리고 나중에 다시 마이그레이트 명령어가 실행되면 이곳에 저장된 파일 이름의 마이그레이션 파일은 이미 적용했다고 그 외의 파일을 적용시킨다.즉, 사전에 우리 프로젝트는 이미
0001_initail.py
부터0045_some_coding.py
까지의 마이그레이션 파일이 적용되었고 django는 django_migrations를 통해 이 이름들의 마이그레이션 파일을 제외한 나머지 파일들에 대해서 마이그레이트한다. 따라서 내가 4-2)에서 만든0001_initial.py
은 무시할 것이고0002_my_app_comment.py
라는 파일을 적용할 것인데 해당 팔이0001_initial.py
을 참조하고 있으니 해당 파일의 내용부터 차례대로 DB에 적용할 것이다. 여기서는 두개의 파일만 고려했지만 참조하는 파일이 많아진다면 마이그레이션 파일간의 이름이 기존에 적용했던 마이그레이션 파일 0002_라는 번호가 겹치므로과 혼동을 야기시킬 수 있다.
최대한 혼동을 줄일 수 있는 방법으로는 마이그레이션 파일 이름을 새로운 번호로 설정하는 것이다. 즉
0002_my_app_comment.py
로 지었던 이름을0046_my_app_comment.py
이런 식으로 지어보자. 그리고pyhon manage.py migrate my_app
를 실행하면 다음과 같이 django_migrations 테이블에 다음과 같이 데이터가 추가된다.이러면 다른 개발자가 보더라도 새로운 마이그레이션 파일이 추가되었음을 알 수 있고 헷갈리지않고 최신화를 유지할 수 있을 것이다.