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)를 추가하기