[Django] ManytoManyField 중간 테이블을 잃어버렸어요
ManyToManyField 와 migrations 에 대한 이야기
개요
models.py 수정 및 로컬DB에서 테스트 후 라이브 배포를 진행하던 날.
migrate 에 오류는 없었으나 실제 Database에는 해당 테이블이 없는 이슈가 발생했다.
내 테이블을 훔쳐간 그녀석을 10년뒤에 어쩌고..
ManyToManyField 사용 시 생성되는 중간테이블에 관련한 이야기이다.
ManyToManyField
다대다 관계
두 개의 모델 사이에 다수의 관계를 설정할 수 있게 도와주는 필드이다.
class Pizza(models.Model):
name = models.CharField(max_length=64)
price = models.IntegerField(default=0)
topping = models.ManyToManyField('Topping', blank=True)
class Topping(models.Model):
name = models.CharField(max_length=64)
위처럼 피자와 토핑간의 관계로 설명할 수 있다.
피자의 종류에는 치즈피자, 불고기피자, 페퍼로니피자, 콤비네이션피자, 하와이안피자, 쉬림프피자 등 수많은 피자가 있다.
토핑에도 페퍼로니, 양파, 버섯, 불고기, 고구마, 감자, 치즈 등 수많은 토핑이 있다.
불고기피자에는 불고기만 들어가지 않고, 치즈는 치즈피자에만 들어가지 않듯 서로가 다양한 값들에 엮일 수 있는 관계를 다대다 관계로 이해할 수 있다.
ManyToManyField in SQL
show tables
를 했을 때 위처럼 편하게 나오면 좋겠지만 ManyToManyField는 Django ORM에서 편하게 사용하기 위해 만들어진 필드로 실제 SQL로 확인해보면 의문의 테이블이 추가되어 있다.
# show tables
pizza
pizza_topping
topping
난 models.py 에 pizza와 topping 모델만 정의했는데 이름이 섞인 의문의 테이블이 생겨져있다.
그렇다. Django 는 ManyToManyField를 SQL에서 이해할 수 있도록 중간테이블을 생성해주고 있다.
단순히 중간 테이블의 역할이라 관계만을 보여주는 테이블이라서 해당 테이블에 추가적인 컬럼은 사용할 수 없다.
“음..관계가 형성된 시점이나 추가한 사람을 확인하고 싶은데?”
ManyToManyField 에 through
옵션을 사용하면 중간 테이블까지 직접 컨트롤 할 수 있으니 걱정하지 않아도 된다
class Pizza(models.Model):
name = models.CharField(max_length=64)
price = models.IntegerField(default=0)
topping = models.ManyToManyField('Topping', through="PizzaToppingRelation")
class Topping(models.Model):
name = models.CharField(max_length=64)
class PizzaToppingRealtion(models.Model):
pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)
topping = models.ForeignKey(Pizza, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
위처럼 through 사용 시 중간테이블을 대체하는 테이블을 직접 설정할 수 있다.
django는 다 계획이 있구나
그래서 테이블이 어디갔다고?
본론으로 돌아와서, ManyToManyField 를 설정하고 migration 을 진행했는데 실제 DB에는 중간테이블이 없었다.
migrate 과정에도 오류가 없었고 show migrations
확인시에도 해당 app 의 migration 이 이루어졌다.
애꿎은 migrations 파일만 지웠다가 makemigrations, migrate 반복하던 와중..
migrations 파일에 ManytoManyField 수정 내용이 포함되지 않은것을 확인했다.
migrations/0001_initial.py
models.py 가 수정되고 makemigrations 명령어를 실행하면 생성되는 파일이다.
기존 migration 버전과 비교하여 변경 사항을 알려주는 파일로, 해당 파일을 기반으로 migrate 가 진행된다.
아마도 기존 migrations 파일이 존재하지 않은 상태에서 makemigrations를 실행하여 ManytoManyField가 추가된게 아닌 기존에 있던 컬럼으로 인식한게 아닐까 싶다.
즉, models.py 에 변화가 없다고 판단하여 models.py 의 상태만 그대로 반영하여 생성한것이다.
정상적으로 필드 추가 내용이 반영되었다면 아래와 같이 AddField 가 migrations 파일에 포함되어 있어야 한다.
class Migration(migrations.Migration):
dependencies = [
('app', '0002_add_topping_option'),
]
operations = [
migrations.AddField(
model_name='pizza',
name='topping',
field=models.ManyToManyField(blank=True, to='app.Topping'),
),
]
급하게 수정하면서
migration이 꼬였다고 라이브 DB를 막 건드릴수는 없기에 편법으로 migration 을 복구하는 방법을 알아냈다.
해당 컬럼을 지우고 migration을 진행한 뒤 해당 컬럼을 만들어서 migration을 진행해라
마치며
그냥 평소에 migrations 파일 관리를 잘하자…