생성된 마이그레이션 파일의 SQL 실행 계획을 보여준다.
CashHistory
모델에 virtual_cash라는 새 컬럼을 추가하려고 한다.
# models.py
class CashHistory(models.Model):
virtual_cash = models.IntegerField(default=0)
...생략...
Migrations for 'cash':
cash/migrations/0060_cashhistory_virtual_cash.py
- Add field virtual_cash to cashhistory
Linting for 'cash':
(cash, 0060_cashhistory_virtual_cash)... ERR
NOT NULL constraint on columns
The migration linter detected that this migration is not backward compatible.
- If you keep the migration, you will want to fix the issue or ignore the migration.
- By default, the newly created migration file will be deleted.
Do you want to keep the migration? [y/N]
migration linter 패키지
가 경고를 보내고 있다.
backward 호환성이 없다는 것이다.
backward 호환성?
그게 뭔데?
예를 들어 보자.
v1코드가 현재 배포되어 서비스 중이다.
migration 파일이 담긴 v2코드를 배포하려 한다.
로드밸런서가 v1에 트래픽을 라우팅하고 있다.
v2 서버가 실행되고 장고 서버가 실행되기 전에 운영 DB에 마이그레이션 명령어가 실행된다.
마이그레이션이 반영되어 DB 스키마가 변경됐다.
새로 적용될 컬럼에 대한 정보가 v1의 코드에 없다.
v1에서 지속적인 에러가 발생할 것이다.
다급해진 개발자는 관성적으로 롤백을 생각한다.
하지만 이는 최악의 선택이다.
v2코드가 얼른 배포되어 배포가 종료돼야 해결되는 문제이기 때문이다.
아래 sqlmigrate을 통해 무엇이 문제가 되는지 확인해 보자.
python manage.py sqlmigrate cash 0060_cashhistory_virtual_cash
결과
BEGIN;
--
-- Add field virtual_cash to cashhistory
--
ALTER TABLE `cash_history` ADD COLUMN `virtual_cash` integer DEFAULT 0 NOT NULL;
ALTER TABLE `cash_history` ALTER COLUMN `virtual_cash` DROP DEFAULT;
COMMIT;
virtual_cash
컬럼을 생성한다.디폴트 값을 제거한 순간, 애플리케이션의 코드가 디폴트 값 생성에 책임이 있다. v2 코드에는 row에 데이터를 insert할 때, 새로 생긴 컬럼에 어떤 필드가 있으면 좋을지 알고 있다. 하지만, v1 코드에는 그러한 정보가 없다.
이 말이 v1에 대해서 backward 호환성이 없다고 말하는 것이다.
생성된 마이그레이션 파일에 아래와 같이 패키지를 적용했다.
from django.db import migrations, models
from django_add_default_value import AddDefaultValue
class Migration(migrations.Migration):
dependencies = [
...생략...
]
operations = [
migrations.AddField(
model_name="cashhistory",
name="virtual_cash",
field=models.IntegerField(default=0),
),
AddDefaultValue(
model_name="cashhistory",
name="virtual_cash",
value=0,
),
]
BEGIN;
--
-- Add field virtual_cash to cashhistory
--
ALTER TABLE `cash_history` ADD COLUMN `virtual_cash` integer DEFAULT 0 NOT NULL;
ALTER TABLE `cash_history` ALTER COLUMN `virtual_cash` DROP DEFAULT;
--
-- Add to field cashhistory.virtual_cash the default value 0
--
ALTER TABLE `cash_history` ALTER COLUMN `virtual_cash` SET DEFAULT '0';
COMMIT;
마지막에 데이터베이스가 DEFAULT 값이 무엇인지 알도록 하고 있다.
즉, v1 애플리케이션 코드 단에서 virtual_cash 값의 정보가 없으니, 데이터베이스에게 위임하는 것이다.