협업을 하다 보면 migration이 꼬일 때가 있다. 그럴 때는 dependencies를 변경해 주든지 파일명을 변경해 주든지 해야 하는데 무엇보다 migration tree를 깔끔하게 관리하는 것이 중요하다. 만약 이미 prod에 배포된 상태라면 migration tree를 변경하는 것이 어려울 수도 있지만 개발 환경이라면 깔끔하게 변경을 해두는 것이 좋다.
Squash는 현재 가지고 있는 migrations 파일들을 하나 또는 적은 숫자로 줄이는 것을 말한다. 당연히 줄임으로써 생기는 예상치 못한 변경 사항은 없어야 한다.
장고는 일단 프로젝트 상에 있는 모든 migrations를 가져온 뒤 거기서 Operations를 추출해 내고 이를 순서대로 정렬한다. 그러면 optimizer가 돌아가며 해당 리스트의 길이를 줄일 수 있다면 줄이려 할 것이다.
가령, CreateModel
, DeleteModel
은 서로 상쇄될 수 있으며 AddField
는 CreateModel
으로 들어갈 수 있다.
그리고 장고는 이 migrations를 줄일 수 있을 만큼 줄일 것이고 줄어드는 양은 각 모델들이 얼만큼 연결되어 있는 지에 달려 있다.
프로세스는 다음과 같이 진행 된다.
$ ./manage.py squashmigrations myapp 0004
Will squash the following migrations:
- 0001_initial
- 0002_some_change
- 0003_another_change
- 0004_undo_something
Do you wish to proceed? [yN] y
Optimizing...
Optimized from 12 operations to 7 operations.
Created new squashed migration /home/andrew/Programs/DjangoTest/test/migrations/0001_squashed_0004_undo_something.py
You should commit this migration but leave the old ones in place;
the new migration will be used for new installs. Once you are sure
all instances of the codebase have applied the migrations you squashed,
you can delete them.
squashmigrations --squashed-name <SQUASHED_NAME>
주의해야 할 점이 있는데, 이렇게 sqashmigrations 명령어를 실행하면 원하는 대로 생성되지 않을 수도 있다. 즉, mis-optimized 될 수도 있고 CircularDependencyError가 발생할 수도 있다. 이렇게 될 경우 개발자가 직접 해결해 주어야 한다.
위와 같이 django의 squashmigrations 명령어를 이용하는 방법도 있지만 개발자가 직접 수동으로 진행하는 방법도 있다. 위의 경우처럼 무언가 꼬이는 상황을 피하려면 직접 하는 것이 나을 수도 있다.
myapp 앱에 002, 003-1, 003-2의 세 가지 마이그레이션이 있다고 가정해 보자.
이 때, 003-1은 002에서 모델을 분리한 것이고 003-2는 필드의 max_length를 늘리는 작업이었다.
그렇다면 003-1, 003-2는 곁가지 작업 같은 것이기 때문에 굳이 migration 파일을 새로 만들어 놓을 필요가 없다.
따라서, 아래와 같이 직접 sqash를 한다.
002로 migrate를 한다.
python manage.py migrate myapp 002 --settings=...
003 이후의 migration 파일들을 모두 지운다.
migration 파일을 새로 만든다.
python manage.py makemigrations
그러면 좀 더 tree 구조가 예쁜, 협업에 좋은 migrations 구조를 갖게 될 것이다!!!