전에 여기서 내가 적은 내용은 다음과 같다.
하지만, 이미 게시글을 올리는 유저 라는 뜻으로 User를 FK로 지정해 줬었다. 한번 FK 지정에 사용한 class를 별도 옵션 없이 또 다시 지정하려니 다음과 같은 에러가 발생했다.
(대충 에러 메시지 사진)
django 에서는 한가지 class를 서로 다른 용도로 FK 지정을 해준다 하여도 그 두가지를 비교할 수 없다. User class에서 게시글을 올린 사람을 뜻하는 user인지, 그 게시글을 '좋아요'한 user인지 구분 X 이럴때 새로운 이름을 부여해 준다. related_name !!class Posting(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE) image_url = models.URLField(max_length=2000) description = models.CharField(max_length=100, null=True) create_at = models.DateTimeField(auto_now_add=True) * like_user = models.ManyToManyField(User, through='Like', related_name='like_user') class Meta: db_table = 'postings'
게시글 올린이와 구분하기 위해 user가 아닌 like_user라고 이름지었다.
'Like' class를 통해 User와 MtoM 관계임을 명시하고, 이때 사용되는 user는 like_user라고 related_name을 지었다.
결론 = 틀렸다.
진정한 related_name의 의미를 반대로 생각했던 것.
장고쉘로 더 많이 두들겨봤으면 알 수 있었을텐데 말이다.
처음 "어랏...?" 을 느낀건 sweethome models.py 관련 리뷰를 받고나서이다..
왜 저런 질문을 하셨는지 유추부터 땡
"그렇다면 related_name 이름을 잘못지정하셨습니다 하핫
여기 붙이는 이름은 해당 클래스를 지칭해야합니다."
참고했던 소헌님의 예시를 다시 살펴보면 다음과 같다.
class User(models.Model):
name = models.CharField(max_length = 50)
job = models.ForeignKey(
'Occupation',
on_delete = models.CASCADE,
related_name = 'appliers'
flow 설명 전 심호흡,,,
FK로 Occupation을 갖고있는 job의 입장에서 User는 지원자이다. 따라서 related_name = appliers
끝!
본래 내 코드는 다음과 같았다.
class Posting(models.Model):
user = models.ForeignKey('user.User')
#생략
like_user = models.ManyToManyField('user.'User', through='Like' related_name='posting_like_user')
이걸 참고한 코드의 상황에 맞춰 생각하면, 그리고 "해당 클래스를 지칭해야합니다"에 맞다면
class Posting(models.Model):
user = models.ForeignKey('user.User')
#생략
like_user = models.ManyToManyField('user.'User', through='Like' related_name='user_like_posting')
이 된다.
본래 내 코드는 다음과 같다
class Follow(models.Model):
follower = models.ForeignKey('User', on_delete=models.CASCADE, related_name='follower')
following = models.ForeignKey('User', on_delete=models.CASCADE, related_name='following')
엉망 진창이라 설명할 수도 없다. 바로 수정!
class Follow(models.Model):
user = models.ForeignKey('User', on_delete=models.CASCADE)
following = models.ForeignKey('User', on_delete=models.CASCADE, related_name='follower')
일단 related_name으로 follower를 쓸거기 때문에 기존에 follower라고 이름지어졌던 field의 이름을 user로 바꾸었다. (보기 편하려고)
User를 FK로 갖고 있는 following field (얘도 user임) 입장에서 Follow 의 내용(user field의 내용=user filed에 들어갈 user)은 follower를 의미한다.
따라서 following field의 related_name 은 follower가 된다.
+----+--------------+---------+
| id | following_id | user_id |
+----+--------------+---------+
| 1 | 2 | 1 |
| 2 | 1 | 3 |
+----+--------------+---------+
>>> one = User.objects.get(id=1)
>>> two = User.objects.get(id=2)
>>> three = User.objects.get(id=3)
>>> one.follower.values()
<QuerySet [{'id': 2, 'user_id': 3, 'following_id': 1}]>
>>> one.follow_set.values()
<QuerySet [{'id': 1, 'user_id': 1, 'following_id': 2}]>
>>> two.follower.values()
<QuerySet [{'id': 1, 'user_id': 1, 'following_id': 2}]>
>>> two.follow_set.values()
<QuerySet []>
아무래도 user field의 related_name을 지정해주지 않았기 때문에 기본으로 follow_set이라고 명명되었다.
one의 follower를 무사히 불러왔다!!
그런데 field 이름이 user 인것이 좋아보이지 않는다. 다시 field 이름을 user에서 follower로 바꾸자
class Follow(models.Model):
follower = models.ForeignKey('User', on_delete=models.CASCADE)
following = models.ForeignKey('User', on_delete=models.CASCADE, related_name='following')
+----+--------------+-------------+
| id | following_id | follower_id |
+----+--------------+-------------+
| 1 | 2 | 1 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
| 4 | 1 | 5 |
| 5 | 1 | 6 |
+----+--------------+-------------+
>>> one.follower.values()
<QuerySet [{'id': 2, 'follower_id': 3, 'following_id': 1}, {'id': 3, 'follower_id': 4, 'following_id': 1}, {'id': 4, 'follower_id': 5, 'following_id': 1}, {'id': 5, 'follower_id': 6, 'following_id': 1}]>
>>> one.follow_set.values()
<QuerySet [{'id': 1, 'follower_id': 1, 'following_id': 2}]>
에러는 다음과 같은 때에 발생했다.
>>> one.following.values()
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'User' object has no attribute 'following'
following 이라는 개념은 User class에 없다. follower 가 된건 related_name 때문이라는 것!!!!
그런데 바꾸고 나니 여전히 follow_set이 사용되는게 불만이다.
class Follow(models.Model):
follower = models.ForeignKey('User', on_delete=models.CASCADE)
following = models.ForeignKey('User', on_delete=models.CASCADE, related_name='following')
그래서 지정해 줬다. related_name을
>>> one = User.objects.get(id=1)
>>> two = User.objects.get(id=2)
>>> three = User.objects.get(id=3)
>>> one.follower.values()
<QuerySet [{'id': 2, 'follower_id': 3, 'following_id': 1}, {'id': 3, 'follower_id': 4, 'following_id': 1}, {'id': 4, 'follower_id': 5, 'following_id': 1}, {'id': 5, 'follower_id': 6, 'following_id': 1}]>
>>> one.following.values()
<QuerySet [{'id': 1, 'follower_id': 1, 'following_id': 2}]>
one을 following하는 즉 one의 follower를 찾고 싶다면
one.follower
에서follower_id
를 본다.
one이 following하는 user를 찾고싶다면one.following
에서following_id
를 본다.
follow 관계는 방향성이 있다고 생각한다.
내가 누구를 향하면, 내가 누구를 팔로잉
나를 누가 향하면, 나를 누가 팔로잉 = 나는 그 누구의 팔로워
이를 적용해 field의 이름을 바꿔주었다.
class Follow(models.Model):
from_user = models.ForeignKey('User', on_delete=models.CASCADE, related_name='follower')
to_user = models.ForeignKey('User', on_delete=models.CASCADE, related_name='following')
+----+--------------+------------+
| id | from_user_id | to_user_id |
+----+--------------+------------+
| 1 | 1 | 2 |
| 2 | 3 | 1 |
| 3 | 4 | 1 |
| 4 | 5 | 1 |
| 5 | 6 | 1 |
+----+--------------+------------+
>>> from user.models import *
>>> one = User.objects.get(id=1)
>>> two = User.objects.get(id=2)
>>> three = User.objects.get(id=3)
>>> one.follower.values()
<QuerySet [{'id': 2, 'from_user_id': 3, 'to_user_id': 1}, {'id': 3, 'from_user_id': 4, 'to_user_id': 1}, {'id': 4, 'from_user_id': 5, 'to_user_id': 1}, {'id': 5, 'from_user_id': 6, 'to_user_id': 1}]>
>>> one.following.values()
<QuerySet [{'id': 1, 'from_user_id': 1, 'to_user_id': 2}]>
one을 following하는 즉 one의 follower를 찾고 싶다면
one.follower
에서from_user_id
를 본다.
one이 following하는 user를 찾고싶다면one.following
에서to_user_id
를 본다.
깔끔하다.!
개념 이해 혼자 하려면 답없다. 함께해서 위코드...
ManyToMany 관계에서 related_name을 설정할땐 명확한 3개의 table이 있어 좀더 수월했는데 하나의 class 에서, 같은 class를 각각 다른 의미로 FK로써 받아오는 것에 대해 related_name을 설정하려니까 약간의 뇌정지가 왔던것 같다.
shell 로 다닥다닥 두들겨 보면서 이해하는데 정말정말 많은 도움이 되었다..!
굿~ 다시 한 번 정리하고 갑니다 채록님