django에서 m2m (many to many field)를 쓰는 경우는 흔합니다. 이 필드는 mapping table을 생성하는데요. 이 mapping table에서 filter를 어떻게 거는지 간단하게 알아봅시다.

 


 먼저, post에 좋아요 기능을 추가하려고 합니다. 유저 1명은 여러 포스트에 좋아요를 누를 수 있고, 포스트 하나도 유저 여러 명이서 좋아요를 받을 수 있습니다. 즉, post와 user는 다대다 관계인 셈입니다. 그래서, Post model은 아래와 같이 바꿀 수 있습니다.

 

 보시면, like가 걸려 있는데, Post와 User간 Many to Many로 연결됨을 의미합니다. 실제로, migrate를 하고 나서, 몇 개의 좋아요 데이터를 추가했습니다. 새로 생성된 테이블을 보면 아래와 같습니다.

 

 post_id가 8이고, user_id가 3인 레코드, post_id가 7이고, user_id가 1인 레코드 이렇게 2개가 존재합니다. 당연하게도, 이러한 구조의 테이블인 경우, 포스트 7번에 여러 명이 좋아요를 누른다고 해도, 문제 없이 처리가 가능할 겁니다. 역으로 유저 하나가 여러 포스트에 좋아요를 누른 것도 처리가 가능합니다.

 

 

 myapp_post_like와 auth_user를 inner join해서 보겠습니다.

 

 

 그러면 username이 alpha인 유저는 8번 post에 좋아요를 눌렀고, admin인 유저는 7번 포스트에 좋아요를 눌렀음을 볼 수 있어요.

 


 우리가 원하는 것은, 유저 이름이 "alpha"인 유저가, 좋아요를 누른 포스트 id를 모두 긁어오는 것입니다. 어떻게 하면 될까요? 문서를 읽어 보면, through 라는 키워드로 접근하면 됨을 알 수 있어요. 읽다 보면, Model.m2m_field.through.objects.all() 이라는 것이 보이는데요. 이 때 through는 m2m field가 표현된 테이블 (모델)임을 알 수 있어요. 이게 무슨 소리인가?

 

 

 17번째 줄을 보면, Post.like.through가 있어요. 여기서 m2m_field는 like입니다. 그러면, m2m field가 표현된 테이블(모델)에 접근하는 것은 Post.like.through입니다. 이를 그림으로 도식화 해 보면 아래와 같습니다.

 

 이렇게 함으로써, User와 Post를 mapping 해주는 table에 직접 접근이 가능합니다. 이제, 문제는 좋아요를 클릭한 유저의 이름이 "alpha"인 포스트의 아이디만 불러오는 것입니다. 그러면 filter 조건에 무언가를 걸어야 겠는데요. 문제 상황은 Post 내에, m2m 필드가 있었는데, 그 필드가 다른 모델인 User를 참조하고 있었다는 것입니다. 다시 문서의 if the source 단락을 봅시다.

 

 throght_fields 라는 특별한 설정이 없는 한, 디폴트 설정으로 들어가기는 할 텐데요. containing model하고 other model이 다른 경우입니다. containing model은 m2m이 선언된 모델, other model은 m2m이 가리키는 모델이에요.

 

 

 그림으로 그리면 이런 상황이기 때문입니다. 그래서, following field가 user_id, post_id 이렇게 2개가 생성됩니다. 당연하게도, 이제 우리는 user_id로 필터링을 걸면 되고, 값을 가져올 때에는 post_id로 가져오면 됩니다. 그래서, 18번째 줄에 filtering 조건에 user_id=user.id를 넣었습니다. 그리고, 21번째 줄에 like_data를 순회할 때 like.post_id로 포스트 아이디를 가지고 옵니다.

 

 실행 결과는 위와 같습니다. 정리하면, m2m이 선언되어 있는 모델이 m1, 참조하는 모델이 m2라 하고, 이 둘이 서로 다른 모델이라면 following field가 m1_id, m2_id 이렇게 2개가 생기게 됩니다.

 


 그런데, 우리는 같은 모델에 대해서 m2m field를 걸어버리는 경우도 있어요. 예를 들어, User의 팔로우 기능을 추가할 때, 혹은 block 유저를 추가할 때 등이 있습니다. 이 때는 요런 식으로 모델이 잡힙니다.

 

 

 유저 1이 유저 3을 팔로우 했다 등의 정보가 들어가니, mapping table의 두 field는 User를 참조하게 될 겁니다. 문제는, 이 때 참조하는 모델이 같기 때문에 from_<참조 모델 이름>_id, to_<참조 모델 이름>_id로 잡히게 됩니다. 즉, from_user_id, to_user_id 이렇게 2개로 잡히게 됩니다.

 

 여기에서는 Follow (따르는) 주체가 from_user_id, Follow 당하는 사람이 to_user_id가 됩니다. 이제, Follow 하는 주체에 대해서 필터가 걸리는지, 당하는 사람에 대해 필터가 걸리는지에 따라 from_user_id, to_user_id가 필터를 걸면 됩니다.