[Django] Migration

작성:    

업데이트:

카테고리:

태그: ,

마이그레이션(Migrations)

  • django에서 class로 데이터를 받을 때, 이는 django 차원에만 머무는 데이터이다.
  • 이를 반드시 실제 DB Table에 반영해야 한다!
  • 이때 Table에 데이터를 mapping 시켜주는 과정


Migration 명령어

  • 실행 및 DB 스키마를 다루기 위함
  • git bash에서 python manage.py ~~ 뒤에 오는 것


makemigrations ⭐

  • model을 변경한 것에 기반한 새로운 migration(설계도)을 만들 때 사용
  • git과 같이 models.py의 해당 시각의 모습을 그대로 찍어서 스키마를 저장
$ python manage.py makemigrations [app_name]
  • app_name을 생략하면 전체 app에 대해서 migration 생성
  • 하지만 생략하는 경우 의도치 않은 migration이 생길 수 있으므로 app_name은 같이 입력하는 것을 권장


migrate ⭐

  • migration을 DB에 반영하기 위해 사용
  • 모델에서의 변경 사항들과 DB의 스키마가 동기화
$ python manage.py migrate [app_name] [migration_name]
  • app_name만 지정하면 특정 app만 migration 가능
  • migration filename까지 지정하면 해당 번호의 마이그레이션 적용
  • 이전 버전으로 되돌리는 것도 가능!!


sqlmigrate

$ python manage.py sqlmigrate [app_name] [migration_name]
  • migration에 대한 SQL 구문을 보기 위해 사용
  • migration이 SQL 문으로 어떻게 해석되어서 동작할지 미리 확인 가능


showmigrations

$ python manage.py showmigrations [app_name]
  • 프로젝트 전체의 migration 상태를 확인하기 위해 사용
  • migration 파일들이 migrate 되었는지 여부 확인 가능


주의사항 : Migration의 삭제

Migration 파일은 절대로 삭제하지 않는다! 이전 버전에 대한 의존성을 가지기 때문

  • 하나의 Migration 파일은 해당 migration이 생성된 시점의 model의 구조(DB 스키마)
  • git처럼 버전을 나눠서 관리할 수 있다.
  • 이전 버전에 대한 의존성을 가진다!
  • 이전 버전을 함부로 제거하면 최신 migration file도 문제가 생길 수 있다.


그러면 어떻게 삭제해야 하나요?

상황 : 0001initial과 0002_auto… migration file이 있고, 0002 file을 제거하고 싶다.

  1. $ python manage.py migrate app_name 0001 명령어로 이전 버전으로 적용 후 삭제
  2. $ python manage.py migrate app_name zero 명령어로 migration 초기화 후 삭제



활용 실습

1. models.py 작성 : 스키마 클래스 정의

# articles.models.py

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=10)
    content = models.TextField()
  • 속성으로 title과 content를 가지고, 각각 10자 이내의 문자열과 문장을 datatype으로 가진다고 제한
  • 모든 스키마는 modles.Model 클래스를 상속하는 서브클래스


2. makemigrations : 현재 models.py 상태를 설계도로 저장

$ python manage.py makemigrations
  • 현재 models.py 상태를 설계도로 저장한다.
  • 아직까지는 DB에 적용된 것이 아닌 django에만 머물러 있는 설계도이다.


Migrations for 'articles':
  articles\migrations\0001_initial.py
    - Create model Article
  • 위의 문자들이 terminal에 뜨면서 models.py에 정의한 클래스와 인자로 뭔가를 만들었다.
  • 이 작업으로 app의 migrations 폴더에 0001_initial.py 파일이 생겼다.


3. 0001_intial.py : migration 파일 확인

# articles/migrations/0001_initial.py

from django.db import migrations, models

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=10)),
                ('content', models.TextField()),
            ],
        ),
    ]
  • makemigrations 명령어로 생성된 0001_initial.py 파일의 모습이다.
  • migrations.CreateModel 명령어이며, name은 models.py의 className이다.
  • models.py에서 작성한 title과 content 외에도 id가 새로 생겼다.
  • id는 PK로 사용되며, 자동으로 생성된다.


4. migrate : 설계 스키마를 DB에 반영

$ python manage.py migrate [app_name] [migration_name]
  • migration file을 DB에 반영하는 명령어이다.


Operations to perform:
  Apply all migrations: admin, articles, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  ...
  Applying auth.0011_update_proxy_permissions... OK
  Applying auth.0012_alter_user_first_name_max_length... OK
  Applying sessions.0001_initial... OK
  • 위와 같이 terminal에 여러 가지 확인 OK가 뜬다.


비주류 1. sqlite extension : DB 확인

  • VSC의 sqlite extension 다운
  • db.sqlite3 우클릭 후 Open Database 클릭

image

  • 위와 같이 사이드 바에 SQLITE EXPLORER tab이 생긴다.
  • 각 field들의 SQL 방식 datatype 확인 가능
  • PK field 자동 생성 : 데이터 추가될 때마다 1씩 증가되어 추가되는 필드가 자동 생성


비주류 2. sqlmigrate : migration SQL문 확인

$ python manage.py sqlmigrate articles 0001_initial
  • app_name : articles / migration_file : 0001_initial
  • “articles app의 0001_initial migration을 SQL문으로 보겠다!”


BEGIN;
--
-- Create model Article
--
CREATE TABLE "articles_article" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" 
varchar(10) NOT NULL, "content" text NOT NULL);
COMMIT;
  • NOT NULL : NULL일 수 없다. (비면 안 된다.)
  • PRIMARY KEY : PK 설정
  • AUTOINCREMENT : 자동 증가



비주류 3. showmigrations : migration 확인

$ python manage.py showmigrations
admin
     [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
articles
 [X] 0001_initial
auth
 [X] 0001_initial
 [X] 0002_alter_permission_name_max_length
 [X] 0003_alter_user_email_max_length
 ...
 [X] 0011_update_proxy_permissions
 [X] 0012_alter_user_first_name_max_length
contenttypes
 [X] 0001_initial
 [X] 0002_remove_content_type_name
sessions
 [X] 0001_initial
  • [X] 는 안 됐다는 의미가 아니라 되었다는 체크 표시
  • 특정 app의 migrations만 조회하고 싶다면 app_name 추가
$ python manage.py showmigrations articles
articles
 [X] 0001_initial


Model 수정

  • 테이블을 만들 때에는 처음부터 설계를 잘 해서 짜야한다.
  • 나중에 데이터가 있는 상태에서 테이블의 변경을 하려면 큰일이 될 수 있기 때문


DateField’s options

# articles/models.py

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=10)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
  • 기존 Article class에 created_at과 updated_at property를 추가해보자.
  • 이 property들은 DB에서 field로서 작용한다.


auto_now_add

  • 최초 생성 일자
  • Django ORM이 최초 insert(테이블에 데이터 입력)시에만 현재 일자로 갱신


auto_now

  • 최종 수정 일자
  • Django ORM이 save할 때마다 현재 일자로 갱신


DateField가 아니라 DateTimeField 아닌가요?

  • DateTimeField는 DateField의 서브 클래스
  • DateTimeField는 DateField와 동일한 추가 인자(extra argument) 사용
  • argument 추가 없이 DateField와 같은 argument 사용

  • DateField : 날짜
  • DateTimeField : 날짜+시간


Model 수정 후 Migration 시도

$ python manage.py makemigrations

$ python manage.py showmigrations articles
  • models.py에 field를 추가한 뒤, migration 생성
  • articles app의 migration을 보여달라는 명령어 입력


You are trying to add the field 'created_at' with 'auto_now_add=True' to article without a 
default; the database needs something to populate existing rows.

 1) Provide a one-off default now (will be set on all existing rows)
 2) Quit, and let me add a default in models.py
Select an option:
  • 기존 테이블에 있던 값을 default로 어떻게 채워야하는지 알려달라는 오류 메시지

  • 선택 1) 현재 존재하는 모든 값에 대해서 여기서 입력하는 값으로 채워넣겠다.
  • 선택 2) migration 중단하고 default를 따로 조작하겠다.

  • 1번을 선택해보겠다.


Select an option: 1
Please enter the default value now, as valid Python
You can accept the default 'timezone.now' by pressing 'Enter' or you can provide another value.
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now
Type 'exit' to exit this prompt
[default: timezone.now] >>>   
  • 추천하는 default : timezone.now(현재시간)이고 이를 써도 되고, 다른 걸 써넣어도 된다.

  • timezone.now를 입력해보겠다.


[default: timezone.now] >>> timezone.now
Migrations for 'articles':
  articles\migrations\0002_auto_20220308_1130.py
    - Add field create_at to article
    - Add field updated_at to article
  • 0002_auto… migrations file이 추가되었다.


정리 : migration 3단계

가. models.py

model 변경 사항 발생 시


나. $ python manage.py makemigrations

migration 파일 생성


다. $ python manage.py migrate

DB 반영(Model-DB 동기화)

댓글남기기