[Django Error] you cannot alter to or from M2M fields, or add or remove through= on M2M fields

리미·2020년 6월 23일
0

Django ERROR

목록 보기
1/2
post-thumbnail

원인 : Model의 Field 변경

초기 테이블 정의서

처음으로 실무에서 혼자만 DB 설계를 하는 터라,(나빼고 다들 DB 신경안써줘 너무해)
문서화 덕후는 테이블 정의서를 만들면서 설계하기 시작한다

일단 테이블 리스트를 만들어주고
각 테이블 정의서를 만들어준다.
이런 양식하나 만들어놓고 설계하면 편하다(ERD도 필수)

초반엔 departments 컬럼이 아예 존재하지않았다.

그냥 HopeDepartment 테이블안에서 StudentExtends와 연결시키려고했는데, 문득 ManyToManyField를 사용하면 더 직관적이고 Django에서 ORM을 짤때 더 편하게 되지않을까라고 생각이들어서 변경하게 되었다.(진짠지는 모름)

항상 계획과 실행은 다르지

혼자 DB설계하다보니 이미 테이블 정의서를 이용해 짜둔거라도 이렇게 모델을 만들게 되면 자주 변경되는 것 같다. 이점은 더 노력해야되는 부분(회고회고)

    departments = models.ManyToManyField(Department, through='HopeDepartment')
    # departments = models.ForeignKey(Department, on_delete=models.CASCADE)

기존 departments 필드를 ForeignKey 관계로 묶었다가 다시 ManyToManyField로 변경하였다.

한 학생이 여러 학과를 희망할 수 있으므로, ManyToManyField를 이용해 묶어주기로 하였다.
그냥 through를 쓰지않고도 자동으로 테이블을 생성해서 사용할 수 있지만, 그렇게 되면 필드는 department하나이기 때문에 우리는 지금 우선순위 필드를 추가로 필요하므로,
중계테이블을 HopeDepartment로 지정해 through로 연결해준다.

# 학과정보
class Department(models.Model):
    university = models.ForeignKey(University, on_delete=models.CASCADE)  # 소속대학
    campus = models.CharField(max_length=10)  # 캠퍼스이름 (본교, 분교, 제2캠퍼스 등)
    undergraduate = models.CharField(max_length=20, null=True)  # 학부명
    department = models.CharField(max_length=30, null=True)  # 학과/전공명
    college = models.ForeignKey(College, on_delete=models.CASCADE)  # 표준계열
    daytime = models.BooleanField()  # 1=주간, 0=야간
    status = models.CharField(max_length=5,
                              choices=(
                              ('기존', '기존'), ('변경', '변경'), ('분리', '분리'), ('신설', '신설'), ('통합', '통합'), ('폐지', '폐지'))
                              )  # 작년과 비교했을때의 학과상태


# 학생상세 정보
class StudentExtends(models.Model):
    student = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, primary_key=True)   # student pk
    student_type = models.CharField(max_length=10, choices=(('학생', '학생'), ('학부모', '학부모'), ('N수생', 'N수생'), ('검정고시', '검정고시')), default='학생')    # 학생구분
    high_school = models.ForeignKey(HighSchool, on_delete=models.SET_NULL, null=True)   # 고둥학교
    grade = models.PositiveSmallIntegerField(choices=((3, '3학년'), (2, '2학년'), (1, '1학년')), default=3)  # 학년
    grade_type = models.CharField(max_length=10, choices=(('인문', '인문'), ('자연', '자연'), ('예체능', '예체능')), default='인문')  # 고등학교 계열
    departments = models.ManyToManyField(Department, through='HopeDepartment')
    # departments = models.ForeignKey(Department, on_delete=models.CASCADE)


# 학생이 희망하는 학과
class HopeDepartment(models.Model):
    priority = models.PositiveSmallIntegerField()  # 우선순위
    department = models.ForeignKey(Department, on_delete=models.CASCADE)
    student_extend = models.ForeignKey(StudentExtends, on_delete=models.CASCADE)

에러의 시작

makemigrations를 하고 migrate를 치면 이러한 에러가 뜨게 된다!

ValueError: Cannot alter field ~~~ into ~~~~ they are not compatible types 
(you cannot alter to or from M2M fields, or add or remove through= on M2M 
fields)

괄호() 부분만 읽어보면 alterg할수 없다고 add 또는 remove 를 하라는데

이미 생성된 필드를 ManyToManyField로 변경하는 경우,
makemigrations를 치면 내부에서 변경된 걸로 치니까 AlterField로 생성이 된다.

해결

그럼 그냥 변경만 해주면된다.
생성된 migrations 파일로 들어가면 문제가 되는 컬럼(에러에 ~~~로 표시한 컬럼)에

migrations.AlterField(
    model_name='studentextends',
    name='departments',
    field=models.ManyToManyField(through='student.HopeDepartment', 
        to='common.Department'),
),

이런식으로 되어있을것이다.
alter가 불가능하다고 하면 현재는 데이터가 없고 DB 설계단계이므로, 그냥 지워주고 다시 생성하면된다.

DB 안에 데이터가 있을경우
개인적으로 데이터가 있을경우에 필드를 삭제하고 fk를 추가하는 식의 행위를 안하는게 좋은 DB설계라고 생각한다...(DBA가 필요한 이유)
그러니 될수있는대로 설계단계에서 시행착오를 많이 겪어야된다고 생각한다.
DB안에 데이터가 있는 데 반드시 이런 행위를 해야되는 경우
아래 3가지 모두 해야한다
1. DB dump를 받고 난 후 (혹시 잘못될 경우 다시 되돌리기 위해)
2. 엑셀로 현재 데이터를 변경할 테이블의 구조에 맞게 작업하고
(엑셀 사용시 FK연결할때 Vlookup 사용 필수)
3. 완성된 데이터를 DB의 변경한 table에 import 시킨다

migrations.RemoveField(
    model_name='studentextends',
    name='departments',
),
# 해당컬럼을 한번 지워주고
migrations.AddField(
    model_name='studentextends',
    name='departments',
    field=models.ManyToManyField(through='student.HopeDepartment', 
        to='common.Department'),
),
# 변경하려는 내용을 적어서 추가해준다.

이렇게 변경해주고 migrate를 치면 정상적으로 작동한다.

profile
Python이 하고싶은데 자꾸 Flutter 시켜서 빡쳐서 만든 블로그

0개의 댓글