Django M:N 관계 - User끼리의 follow 구현하기

 

models.py

class User(AbstractUser):
    followings = models.ManyToManyField('self', symmetrical=False, related_name='followers')

user 모델에 followings 필드를 추가한다.

user 자신과의 관계이기 때문에 'self'라고 작성한다.

symmetrical=True면 대칭으로, 자동으로 팔로잉-팔로워가 같게 반영되는 것이므로 팔로잉,팔로워를 따로 관리하기 위해서 False로 한다.

follow하는 것은 이 필드에 해당되고, 역참조는 followers가 된다.

 

기본 buit-in 유저모델을 사용했다면 user에 필드를 추가할 수 없다. 

이런 것 때문에 built-in user를 상속받아 새로 만든 것이다.

 

 

ManyToManyField로 자동 생성된 중개테이블은 다음과 같다.

같은 모델으로 관계를 만든 것이기 때문에 from, to 필드로 생성되었다.

 

 

accounts - urls.py

    path('<int:user_pk>/follow/', views.follow, name='follow'),

follow url 추가한다.

 

 

 

 

accounts - views.py

@require_POST
def follow(request, user_pk):
    User = get_user_model()
    person = User.objects.get(pk=user_pk)
    if request.user in person.followers.all():
        person.followers.remove(request.user)
    else:
        person.followers.add(request.user)
상대방은 person으로 User.objects.get(pk=user_pk) 이다.
User를 바로 참조하면 안되기 때문에 User= get_user_model()으로 정의한다.
내가(request.user) 그 사람(person)의 팔로워 목록에 있다면 언팔로우를 한다. 언팔로우는 person의 followers에서 나를 삭제한다.
팔로워 목록에 없다면 add로 추가한다.
 
 
 
 
@require_POST
def follow(request, user_pk):
    if request.user.is_authenticated:
        User = get_user_model()
        me = request.user
        you = User.objects.get(pk=user_pk)
        if me != you:
            if me in you.followers.all():
                you.followers.remove(me)
            else:
                you.followers.add(me)
        return redirect('accounts:profile', you.username)
    return redirect('accounts:login')

 

me, you로 보기쉽게 정의해주었다.

자신을 팔로우하면 안되기 때문에 me!=you 조건을 추가한다.

 

 

            if you.followers.filter(pk=me.pk).exists():

exists()으로 하려면 위와 같다.

 

 

 

 

 

accounts - template - profile.html

  <div>
    팔로워 : {{ person.followers.all|length }} / 팔로잉 : {{ person.followings.all|length }}
  </div>

  {% if request.user != person %}
  <div>
    <form action="{% url 'accounts:follow' person.pk %}" method="POST">
      {% csrf_token %}
      {% if request.user in person.followers.all %}
        <input type="submit" value="언팔로우">
      {% else %}
        <input type="submit" value="팔로우">
      {% endif %}
    </form>
  <div>
  {% endif %}

프로필에 팔로우 부분을 추가한다.

내 프로필이 아닌경우 팔로우, 언팔로우 버튼이 뜨도록 했다.

팔로워 목록에 내가 있으면 언팔로우 / 없으면 팔로우 버튼이다.

length를 이용해서 팔로워, 팔로잉 수를 출력할 수도 있다.

 

여기서는 in 으로 followers를 확인하는 문법을 filter로 사용할 수 없다.

filter()으로 괄호로 메서드를 호출해야하는데 dtl에서는 ()으로 메서드를 호출하지 않기 때문이다.

{% if you.followers.filter(pk=me.pk).exists(): %} <<< - 안된다!

 

 

2번이 1번을 follow하고 있는 것을 확인한다.