[Django] ForeignKey와 related_name

ElyaSetinal·2022년 4월 21일
0

Django

목록 보기
1/2

기념비 적인 첫 글의 내용

velog는 만들어놓고, 뭘 적어야 하나 하다가, 그냥 내가 공부하면서 기억해야할 것들을 적는게 제일 좋겠다 싶었다.
그래서 기념적인 첫글은, 열심히 나를 애먹였던 ForeignKeyrelated_name에 대해서


ForeignKey - Models.py 구성

수업을 들으면서 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

모델명을 작성했으니 이제 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_personlast_modify_person 모두 User를 가져오고, Django에서는 동일한 이름을 사용할 수 없기 때문이라고 한다.

내가 역관계 이름을 지정하지 않았기 때문에, created_personUser.{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)

로 지정할 듯 하다.


에러가 터지기 전까지만 해도, ForeignKeyForeignKey.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 추가)

다른 class 끼리면 어떻게 해결해야 할까?

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을 쓸때 조심해야겠다는 생각이 들었다.


요약

  1. 같은 class 내에서 여러개의 ForeignKey를 사용해야 할때는, related_name을 추가하여 역관계 충돌이 일어나지 않게 하기
  2. 다른 class 내에서 같은 ForeignKey.related_name을 사용한다면, 이름을 별도로 지정하거나 %(class)를 추가하기
profile
꿈을 꾸는 하나의 인생

0개의 댓글