TIL_30 | [Django] 키미노 나마에와.. related_name? (feat. reverse relations)

code_sign·2021년 2월 4일
2

django

목록 보기
4/4
post-thumbnail

djangomodels.py에서 클래스를 작성하다가 보면, 너무 헷갈리는 것들 중 하나가 바로!!! ForeignKey!!!

이 녀석을 같이 차근차근 정복해보자.
(그러나 늘 정복당하는건 나였다...?ㅠㅠ)

참조의 두 형제 👬 [정/역]참조

데이터베이스에서 참조(ForeignKey)관계에 있는 두 테이블이 있다.

  • User
  • Car

두 테이블은 N:1 관계라고 가정하고,
Car는 여러 User를 가진다. (UserCar를 참조)

User가 자신이 소유하고 있는 Car가 있다고 하면 아래와 같은 model을 만들 수 있다.

class User(models.Model):
    name   = models.CharField(max_length = 50)
    car    = models.ForeignKey('Car', on_delete = models.CASCADE))

class Car(models.Model):
    name   = models.CharField(max_length = 50)
    color  = models.CharField(max_length = 50)

위와 같이 model이 설정되었다면 UserCar를 갖고 있기 때문에 정참조가 되므로 바로 접근하여 Carname을 알 수 있다.

user1 = User.objects.get(id = 1)
user1.car.name
>>> 'Maserati'

그럼 해당 Car를 가지고 있는 User를 알 수 있을까? (역참조)
생각만 했을땐, 다음과 같이 실행 할 수 있을 것 같다.

car1 = Car.objects.get(id = 1)
car1.user.name

위의 결과가 내가 의도한대로 code_sign이라고 나올까?
댓츠 노노 ❌

Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Car' object has no attribute 'user'

해당 Caruser라는 속성(attribute)를 가지고 있지 않다며 오류를 뱉는다.(웩 🤮)

어떻게 하면 역참조 관계에서 원하고자 하는 값을 얻을 수 있을까?
바로 [class_name]_set 속성을 이용하면 된다.

car1 = Car.objects.get(id = 1)
car1.user_set.name
>>> 'code_sign'

잘나온다!👍👍

근데 우리는 끊임없이 더 좋은 결과를 연구해야하는 백엔드!
프로불편러인 나는 user이라는 이름보다는 owner라는 이름이 더 좋을꺼 같다.
이럴때 필요한 것이 related_name이라는 속성이다.

(제목의 어그로를 담당했던 '너의 이름은?!' 영화)

포스터의 영화는 물론 너무 재밌지만, (TMI지만 타임슬립 너무 좋아!)
내가 원했던 이름을 지정하기 위해선 related_name이 필요하다.

우리가 이미 작성을 마쳤던 model을 다시 수정해보자.

class User(models.Model):
    name   = models.CharField(max_length = 50)
    car    = models.ForeignKey(
    	'Car', 
        on_delete    = models.CASCADE,
        related_name = 'owner' # ------------- attention!
	)

attention 부분을 잘 보면 related_name을 사용하여 owner라고 정의 해줬다.

다시 역참조로 돌아가 조회해보면 우리는 저 이름을 사용하여 접근 할 수 있다.

car1 = Car.objects.get(id = 1)
car1.owner.name
>>> 'code_sign'

이렇게 사용하는 것이 필수는 아니지만,
훨씬 직관적이고 알아보기 쉬운 코드가 됐다!! 🙌

class User(models.Model):
    name   = models.CharField(max_length = 50)
    car    = models.ForeignKey('Car', on_delete = models.CASCADE)
    subCar = models.ForeignKey('Car', on_delete = models.CASCADE, null = True)

이 모델은 migration도 되지 않을테지만, (related_name이 필요하다고 함)
이런 모델이 있다고 하자.

carsubCar의 데이터를 모두 가지고 있다고 하면 데이터를 어떻게 접근해서 가져올 수 있을까?

car1 = Car.objects.get(id=1)
car1.user_set.name

이게 될까?
❌❌❌❌❌❌댓츠 노노!!!!❌❌❌❌❌❌

Why?!
우리가 아무리 User 테이블이 Car를 참조하고 있고 [class_name_set]을 쓰면 역참조가 된다고 하지만 위의 경우에선 user가 어떤 유저인지 모른다!

  • User 클래스의 car인지,
  • User 클래스의 subCar인지...
    그러기 때문에 우리는 같은 테이블을 참조하는 경우 related_name을 써줘야 한다!
class User(models.Model):
    name   = models.CharField(max_length = 50)
    car    = models.ForeignKey(
    	'Car', 
        on_delete    = models.CASCADE,
        related_name = 'owner'
        )
    subCar = models.ForeignKey(
    	'Car', 
        on_delete    = models.CASCADE, 
        null         = True,
        related_name = 'subOwner'
        )
car1 = Car.objects.get(id=1)
car1.owner.name
>>> 'code_sign'
car1.subOwner.name
>>> 'tori'

휴... 이제 잘 작동한다!

너무 머리가 아프다...

괜찮다!
이걸 쓴 나도 아직 머리가 아프니까...

그래도 우리 모두 잘 이해해서 기본 Foreign의 관계뿐만 아니라 다음에 다룰 ManyToMany 관계에서도 잘 적용해보길 바란다!!
(제발!!!!! 😭)

Today, Learned 🧑🏻‍💻

배운점

  • related_name을 쓰는지!!
  • 필수적일때는 왜 필요한지!!
  • 역참조할때 조회 방법

느낀점

  • 잡힐듯 잡히지 않는 related_name...
  • 진짜 편한 접근때문에 Foreign의 관계를 쓰는구나 느낌👍

오늘의 한마디

아직도 밀당하는 related_name... 이제 그만해도 돼..😂

profile
방탈출 좋아하는 코딩덕후

9개의 댓글

comment-user-thumbnail
2021년 2월 4일

오 성준님께서 짱 쉽게 설명해주셔서 바로 머리에 박혔습니다 ^__^ 또 놀러오죠 ㅎㅎ

1개의 답글
comment-user-thumbnail
2021년 2월 5일

그저 갓 ..

1개의 답글
comment-user-thumbnail
2021년 2월 8일

와 성준님 이 블로그 포스트... 눈물 남니다 ㅠ

1개의 답글
comment-user-thumbnail
2021년 2월 10일

잘 보구 갑니다 성준님 이해 쏙쏙

1개의 답글

관련 채용 정보