makemigrations
은
makemigrations
의 실제 작동 방식ProjectState
# django/db/migrations/state.py
class ProjectState:
"""
Represent the entire project's overall state. This is the item that is
passed around - do it here rather than at the app level so that cross-app
FKs/etc. resolve properly.
"""
# django/core/management/commands/makemigrations.py
class Command(BaseCommand):
help = "Creates new migration(s) for apps."
...
def handle(self, *app_labels, **options):
...
# Load the current graph state. Pass in None for the connection so
# the loader doesn't try to resolve replaced migrations from DB.
loader = MigrationLoader(None, ignore_no_migrations=True)
...
# Set up autodetector
autodetector = MigrationAutodetector(
**loader.project_state(),
ProjectState.from_apps(apps),**
questioner,
)
...
# Detect changes
changes = autodetector.changes(
graph=loader.graph,
trim_to_apps=app_labels or None,
convert_apps=app_labels or None,
migration_name=self.migration_name,
)
...
self.write_migration_files(changes)
# django/db/migrations/autodetector.py
class MigrationAutodetector:
"""
Take a pair of ProjectStates and compare them to see what the first would
need doing to make it match the second (the second usually being the
project's current state).
"""
...
def __init__(self, from_state, to_state, questioner=None):
**self.from_state = from_state
self.to_state = to_state**
self.questioner = questioner or MigrationQuestioner()
self.existing_apps = {app for app, model in from_state.models}
def changes(self, graph, trim_to_apps=None, convert_apps=None, migration_name=None):
"""
Main entry point to produce a list of applicable changes.
Take a graph to base names on and an optional set of apps
to try and restrict to (restriction is not guaranteed)
"""
changes = self._detect_changes(convert_apps, graph)
changes = self.arrange_for_graph(changes, graph, migration_name)
if trim_to_apps:
changes = self._trim_to_apps(changes, trim_to_apps)
return changes
Reference
makemigrations
--dry-run
:--dry-run --verbosity 3
:migrate
migrate
migrate [app_name]
migrate [app_name] [migration_number]
git checkout <commit_hash>
와 유사)--plan
: migration을 실제로 실행하지 않고, 어떻게 실행될건지 보여준다.Migration 적용 순서
자세히 살펴보기
migrate seminar 0004
migrate seminar 0004
에 해당하는 sql 문
migration 상태를 기록하는 sql 문
django_migrations db table
migrate시 migration 대상 앱, 시점, 파일 이름을 포함한 migration 상태가 기록된다.
Q. 왜 migrate seminar 0004
에서 DROP DEFAULT를 추가로 실행할까?
- non-nullable field에 default 값을 지정하지 않고, makemigration
실행
# seminar/models.py
class Seminar(models.Model):
...
+++ type2 = models.CharField(
max_length=100, choices=(('A', '세미나'), ('B', '스터디')))
DEFAULT ‘A’
: default값을 적용하여 row를 채우고DROP DEFAULT
: 정의한 모델에는 column에 default값이 없으므로 다시 원래대로 돌려놓는 작업을 한다. (perserve_default=False에서 옴)migrate seminar 0003
seminar 0003
까지 migrate된 상태에서 실행 결과…
migrate seminar 0001 --plan
migrate seminar zero --plan
undo되는 migration이 dependency에 있는 migration을 우선하여 undo함.
0002_user_seminars.py : seminar.0001_squashed~.py에 dependency가 걸려있다.
이후 최근에 적용된 migration 부터 역순으로 undo