velog는 만들어놓고, 뭘 적어야 하나 하다가, 그냥 내가 공부하면서 기억해야할 것들을 적는게 제일 좋겠다 싶었다.
그래서 기념적인 첫글은, 열심히 나를 애먹였던 ForeignKey와 related_name에 대해서
수업을 들으면서 Django의 models.py를 구성해야하는 과제가 나왔다.
모델명은 주어져있었으니, 그 모델에 맞추어서 적절한 Field를 사용하기만 하면 되는 어렵지 않은 문제
글을 작성한 사람과, 글의 최종 수정자를 입력받아야 하니까, Django의 관계필드(Relationship Fields)의 ForeignKey를 사용하고, 이 ForeignKey의 Primary key는 장고에서 제공하는 user_model을 사용하기로 했다.
from django.contrib.auth import get_user_model
User = get_user_model()
...
class {class.Name}(models.Model):
...
created_person = models.ForeignKey(to=User, on_delete=models.CASCADE, verbose_name='작성자', null=True, blank=True)
last_modify_person = models.ForeignKey(to=User, on_delete=models.CASCADE, verbose_name='최종 수정자', null=True, blank=True)
모델명을 작성했으니 이제 migrations을 사용해서 DB에 적용시켜주면..
<Terminal>
python manage.py makemigrations
SystemCheckError: System check identified some issues:
ERRORS:
support.{class.Name}.created_person: (fields.E304) Reverse accessor for 'support.{class.Name}.created_person' clashes with reverse accessor for 'support.{class.Name}.last_modify_person'.
HINT: Add or change a related_name argument to the definition for 'support.{class.Name}.created_person' or 'support.{class.Name}.last_modify_person'.
support.{class.Name}.last_modify_person: (fields.E304) Reverse accessor for 'support.{class.Name}.last_modify_person' clashes with reverse accessor for 'support.{class.Name}.created_person'.
HINT: Add or change a related_name argument to the definition for 'support.{class.Name}.last_modify_person' or 'support.{class.Name}.created_person'.
산뜻하게 'migrations가 불가능해요' 라고 말해주는 에러메시지
처음에는 변수명을 잘못 설정해서 그런가 하다가, 인터넷에 찾아보니 related_name이 없어서 역관계를 다시 가져오기때문에, 동일 class에서 created_person과 last_modify_person 모두 User를 가져오고, Django에서는 동일한 이름을 사용할 수 없기 때문이라고 한다.
내가 역관계 이름을 지정하지 않았기 때문에,
created_person은User.{class_Name}_set.all()을 가지게 되고,last_modify_person또한User.{class_Name}_set.all()을 가지게 되는 것동일한 이름이라, Django에서는 불가능하다고 메시지를 출력한 것
공식문서를 읽는 도중에, 역관계를 생성하지 않도록 하려면 related_name='+'로 하거나, related_name='~~~+'로 하라는 설명이 있었다.
역관계가 문제가 되는 시점이니까, related_name='+'를 사용하기로 했다. 대다수의 검색결과는 related_name을 지정하는게 일반적이였지만, 이 필드가 역관계를 어떻게 사용하는지 아직 가늠이 가지 않아서...
from django.contrib.auth import get_user_model
User = get_user_model()
...
class {class.Name}(models.Model):
...
created_person = models.ForeignKey(to=User, related_name='+', on_delete=models.CASCADE, verbose_name='작성자', null=True, blank=True)
last_modify_person = models.ForeignKey(to=User, related_name='+', on_delete=models.CASCADE, verbose_name='최종 수정자', null=True, blank=True)
이렇게 related_name을 사용하니, migrations도 무리없이 진행되고, 별도의 에러메시지 없이 진행되었다.
만약 related_name을 지정해주는 방향으로 진행했다면
created_person = models.ForeignKey(to=User, related_name='created_persons', on_delete=models.CASCADE, verbose_name='작성자', null=True, blank=True)
last_modify_person = models.ForeignKey(to=User, related_name='last_modify_persons', on_delete=models.CASCADE, verbose_name='최종 수정자', null=True, blank=True)
로 지정할 듯 하다.
에러가 터지기 전까지만 해도, ForeignKey의 ForeignKey.related_name가 무슨 의미를 가지는지 이해가 잘 되지 않았는데, 에러가 나고 검색해보니 '이게 이런 의미구나' 싶었다. 역관계를 가지고 여러 데이터가 왔다갔다 하는 경우도 있는거 같으니 아마 앞으로 ForeignKey를 사용할 일이 생기면 반드시 related_name을 사용하지 않을까 싶다.
<참고 사이트>
공식문서 - ForeignKey
공식문서 - ForeignKey.related_name
stackoverflow.com/questions/41595364
stackoverflow.com/questions/2991365
Django 26. 장고 related_name 설정방법
django의 관계형 모델필드
장고(Django)핥짝 맛보기
18:38 추가
stackoverflow.com/questions/22538563
동일 class가 아니라 다른 class 끼리 같은 related_name을 사용해서도 안된다는걸 알게되었다.
class {class.Name}(models.Model): users = models.ForeignKey(User, related_name = 'rn') ... class {class.Name2}(models.Model): users2 = models.ForeignKey(User, related_name = 'rn') ...이런식으로 작성하면
users는 역관계로User.rn.all()을 가지고,users2또한User.rn.all()를 가진다는 의미일까?
공식문서를 읽다보니
User가 아니라, 그냥related_name이 들어가는 듯 하다..
users의 역관계 명은rn_set이 되는거고,users2또한rn_set으로 지정되는 것 같다.
(22.05.16 15:55 추가)
ForeignKey를 사용하는 모델명마다 related_name을 서로 다르게 지정하는 방법도 있지만, 몇백줄, 몇만줄만 넘어가도 내가 그걸 다 기억하면서 할까 싶었다. 한가지 쉬운 방법으로 stackoverflow에 따르면, 해결 방법은 related_name에 %(class)s를 추가하라고 한다.
class {class.Name}(models.Model):
users = models.ForeignKey(User, related_name = '%(class)s_rn')
...
class {class.Name2}(models.Model):
users2 = models.ForeignKey(User, related_name = '%(class)s_rn')
...
같은 class에서만 주의하면 될 줄 알았는데.. 다른 class에서도 related_name을 쓸때 조심해야겠다는 생각이 들었다.
ForeignKey를 사용해야 할때는, related_name을 추가하여 역관계 충돌이 일어나지 않게 하기ForeignKey.related_name을 사용한다면, 이름을 별도로 지정하거나 %(class)를 추가하기