2 minute read

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 파일 관리를 잘하자…

Tags:

Updated: