[Django/DB] 팔로우(Follow) 기능 구현하기 : User-User (M:N) 관계 설정

문지은·2023년 5월 9일
0

Django + Database

목록 보기
7/12
post-thumbnail

User 자기 자신과의 M:N 관계 설정을 통해 팔로우 기능을 구현해보자.

Profile

먼저 자연스러운 follow 흐름을 위해 프로필 페이지를 작성해보자.

url 및 view 함수 작성

# accounts/urls.py

from django.urls import path
from . import views

app_name = 'accounts'
urlpatterns = [
    ...,
    path('profile/<username>/', views.profile, name='profile'),
    
]
# accounts/views.py

from django.contrib.auth import get_user_model

def profile(request, username):
    User = get_user_model()
    person = User.objects.get(username=username)
    context = {
        'person' : person,
    }
    return render(request, 'accounts/profile.html', context)

템플릿 작성

  • Profile 템플릿
<!-- accounts/profile.html -->

{% extends 'base.html' %}

{% block content %}
    <h1>{{ person.username }}님의 프로필</h1>
    <hr>

    <h2>{{ person.username }}'s 게시글</h2>
    {% for article in person.article_set.all %}
        <div>{{ article.title }}</div>
    {% endfor %}
    <hr>

    <h2>{{ person.username }}'s 댓글</h2>
    {% for comment in person.comment_set.all %}
        <div>{{ comment.content }}</div>
    {% endfor %}
    <hr>

    <h2>{{ person.username }}'s 좋아요한 게시글</h2>
    {% for article in person.like_articles.all %}
        <div>{{ article.title }}</div>
    {% endfor %}
    <hr>

    <a href="{% url 'articles:index' %}">back</a>

{% endblock content %}
  • Profile 템플릿으로 이동할 수 있는 하이퍼 링크 작성
<!-- base.html -->

<body>
    <div id="nav">
        {% if user.is_authenticated %}
        <a href="{% url 'accounts:logout' %}">로그아웃</a>
        <a href="{% url 'accounts:update' %}">회원정보수정</a>
      	<!-- Profile 템플릿으로 이동할 수 있는 하이퍼 링크 작성 -->
        <a href="{% url 'accounts:profile' user.username %}">내 프로필</a>
        <form action="{% url 'accounts:delete' %}" method="POST">
            {% csrf_token %}
            <input type="submit" value="회원탈퇴">
        </form>
        <h3 id="user-hello"><i>안녕하세요, {{user}} 님 !</i></h3>
        {% else %}
        <a href="{% url 'accounts:signup' %}">회원가입</a>
        <a href="{% url 'accounts:login' %}">로그인</a>
        {% endif %}
    </div>
    
    <hr>
    <div id="content">
        {% block content %}{% endblock content %}
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
    </body>
<!-- articles/index.html -->

{% extends 'base.html' %} 

{% block content %}
    <h1>INDEX</h1>
    <a href="{% url 'articles:create' %}">작성하기</a>
    <hr />

    {% for article in articles %}
    <p>
        [{{article.id}}] 
        <a href="{% url 'articles:detail' article.pk %}" id="article-title">{{article.title}}</a>
        <!-- Profile 템플릿으로 이동할 수 있는 하이퍼 링크 작성 -->
        <b>- 작성자: <a href="{% url 'accounts:profile' article.user.username %}">{{article.user}}</a></b>
    </p>
    {% if request.user.is_authenticated %}
        <div>
        <form action="{% url 'articles:likes' article.pk %}" method="POST">
            {% csrf_token %}
            {% if request.user in article.like_users.all %}
            <input type="submit" value="안좋아해!">
            {% else %}
            <input type="submit" value="좋아해!">
            {% endif %}
        </form>
        </div>def profile(request, username):
    User = get_user_model()
    person = User.objects.get(username=username)
    context = {
        'person' : person,
    }
    return render(request, 'accounts/profile.html', context)
    {% endif %}
    <hr />
    {% endfor %} 

{% endblock content %}

출력 확인

Follow

모델 관계 설정

  • ManyToManyField 작성 및 Migration 진행
# accounts/models.py

from django.db import models
from django.contrib.auth.models import AbstractUser

# Create your models here.
class User(AbstractUser):
    following = models.ManyToManyField('self', symmetrical=False, related_name='followers')
$ python manage.py makemigrations
$ python manage.py migrate
  • 생성된 중개 테이블 확인

Follow 구현

url 및 view 함수 작성

# accounts/urls.py

from django.urls import path
from . import views

app_name = 'accounts'
urlpatterns = [
    ...,
    path('<int:user_pk>/follow/', views.follow, name='follow'),
    
]
# accounts/views.py

def follow(request, user_pk):
    User = get_user_model()
    person = User.objects.get(pk=user_pk)
    if person != request.user:
        if person.followers.filter(pk=request.user.pk).exists():
            person.followers.remove(request.user)
        else:
            person.followers.add(request.user)
    return redirect('accounts:profile', person.username)

프로필 유저의 팔로잉, 팔로워 수 & 팔로우, 언팔로우 버튼 작성

<!-- accounts/profile.html -->

<h1>{{ person.username }}님의 프로필</h1>
    <hr>

    <div>
        <div>
            팔로잉 : {{ person.followings.all|length }} / 팔로워 : {{ person.followers.all|length }}
        </div>
        {% if request.user != person %}
            <div>
                <form action="{% url 'accounts:follow' person.pk %}" methon="POST">
                    {% csrf_token %}
                    {% if request.user in person.followers.all %}
                        <input type="submit" value="Unfollow">
                    {% else %}
                        <input type="submit" value="Follow">
                    {% endif %}
                </form>
            </div>
        {% endif %}
	</div>

팔로우 버튼 클릭 후 팔로우 버튼 변화 및 테이블 확인

템플릿 수정

  • 부트스트랩 활용 - 개시글과 댓글 카드 형태로 출력하기
<!-- accounts/profile.html -->

{% extends 'base.html' %}

{% block content %}
    <h1>{{ user.username }}님의 프로필</h1>
    <div>
        <div>팔로잉 : {{ user.followings.count }} | 팔로워 : {{ user.followers.count }}</div>    
        {% if request.user != user %}
            <form action="{% url 'accounts:follow' user.pk %}" method="POST">
                {% csrf_token %}
                {% if request.user in user.followers.all %}
                    <button type="submit" class="btn btn-danger">언팔로우</button>
                {% else %}
                    <button type="submit" class="btn btn-primary">팔로우</button>
                {% endif %}
            </form>
        {% endif %}    

    </div>
    <hr>

    <h5>{{ user.username }}님의 작성글</h5>
    <hr>
    {% for article in user.article_set.all %}
        <div class="card">
            <div class="card-body">
                <h5 class="card-title">{{ article.title }}</h5>
                <p class="card-text">{{ article.content }}</p>
                <a href="{% url 'articles:detail' article.pk %}" class="btn btn-primary">자세히 보기</a>
            </div>
        </div>
    {% endfor %}
    <br><br>

    <h5>{{ user.username }}님이 좋아요한 게시물</h5>
    <hr>
    {% for article in user.like_articles.all %}
        <div class="card">
            <div class="card-body">
                <h5 class="card-title">{{ article.title }}</h5>
                <p class="card-text">{{ article.content }}</p>
                <a href="{% url 'articles:detail' article.pk %}" class="btn btn-primary">자세히 보기</a>
            </div>
        </div>
    {% endfor %}

    <br><br>    
    <a href="{% url 'articles:index' %}" class="btn btn-success">목록보기</a>

{% endblock content %}

📍 프로젝트 전체 코드 확인하기

https://github.com/mjieun0956/TIL/tree/master/Database/07.%20User-User%20(Follow)

profile
코드로 꿈을 펼치는 개발자의 이야기, 노력과 열정이 가득한 곳 🌈

0개의 댓글