django
의 models.py
에서 클래스를 작성하다가 보면, 너무 헷갈리는 것들 중 하나가 바로!!! ForeignKey
!!!
이 녀석을 같이 차근차근 정복해보자.
(그러나 늘 정복당하는건 나였다...?ㅠㅠ)
데이터베이스에서 참조(ForeignKey)
관계에 있는 두 테이블이 있다.
두 테이블은 N:1 관계라고 가정하고,
Car
는 여러 User
를 가진다. (User
가 Car
를 참조)
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
이 설정되었다면 User
는 Car
를 갖고 있기 때문에 정참조가 되므로 바로 접근하여 Car
의 name
을 알 수 있다.
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'
해당 Car
는 user
라는 속성(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이 필요하다고 함)
이런 모델이 있다고 하자.
car
와 subCar
의 데이터를 모두 가지고 있다고 하면 데이터를 어떻게 접근해서 가져올 수 있을까?
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
관계에서도 잘 적용해보길 바란다!!
(제발!!!!! 😭)
related_name
을 쓰는지!!related_name
...Foreign
의 관계를 쓰는구나 느낌👍아직도 밀당하는 related_name
... 이제 그만해도 돼..😂
오 성준님께서 짱 쉽게 설명해주셔서 바로 머리에 박혔습니다 ^__^ 또 놀러오죠 ㅎㅎ