Post

Django: ManyToMany 관계 설정과 구현하기

Django에서 ManyToMany 관계를 설정하고 구현하는 방법을 설명합니다. 좋아요와 팔로우 기능을 예제로 다룹니다.

Django: ManyToMany 관계 설정과 구현하기

ManyToMany 관계 설정과 구현하기

ManyToMany 관계란?

다대다(M:N) 관계는 Django에서 models.ManyToManyField를 통해 설정할 수 있습니다. 예를 들어, 좋아요팔로우 같은 기능은 다대다 관계를 이용해 구현됩니다.

다대다 관계를 설정하면, Django는 중계 테이블을 자동으로 생성하여 관계를 관리합니다. 또한, 객체 간의 관계를 쉽게 추가, 제거할 수 있는 메서드(add(), remove())도 제공합니다.

좋아요 기능 구현하기

1. 좋아요의 정의

  • 좋아요란?: 특정 유저가 게시글에 좋아요를 표시하는 기능입니다.
  • 저장해야 할 데이터: 어떤 유저가 어떤 게시글에 좋아요를 눌렀는지.

2. 모델 설정

초기 설정: 1:N 관계 (문제점)

1
2
3
4
5
6
7
8
9
10
11
12
class Article(models.Model):
    title = models.CharField(max_length=50)
    content = models.TextField()
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="articles"
    )
    like_user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="like_articles",
        null=True
    )

문제점: 한 게시글에 여러 유저가 좋아요를 누를 경우를 처리할 수 없습니다.

3. 개선: 2차 구현 (별도의 중계 테이블 추가)

1
2
3
4
5
6
7
class ArticleLike(models.Model):
    article = models.ForeignKey(
        Article, on_delete=models.CASCADE, related_name="likes"
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="likes"
    )

이 설정은 유저와 게시글 간의 좋아요 데이터를 중계 테이블로 저장하여 다대다 관계를 구현합니다.

4. 최종 구현: ManyToManyField 사용

Django는 ManyToManyField를 이용해 중계 테이블을 자동 생성할 수 있습니다.

모델 수정

1
2
3
4
5
6
7
8
9
class Article(models.Model):
    title = models.CharField(max_length=50)
    content = models.TextField()
    author = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="articles"
    )
    like_users = models.ManyToManyField(
        settings.AUTH_USER_MODEL, related_name="like_articles"
    )

중계 테이블: Django가 자동으로 article_like_users라는 중계 테이블을 생성합니다.

5. 좋아요 로직 작성

View 함수 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_POST

@require_POST
def like(request, pk):
    if request.user.is_authenticated:
        article = get_object_or_404(Article, pk=pk)
        if article.like_users.filter(pk=request.user.pk).exists():
            article.like_users.remove(request.user)
        else:
            article.like_users.add(request.user)
    else:
        return redirect("accounts:login")

    return redirect("articles:articles")

URL 설정

1
2
3
4
5
6
7
8
from django.urls import path
from . import views

urlpatterns = [
    ...
    path("<int:pk>/like/", views.like, name="like"),
    ...
]

템플릿 작성 1차 구현: 단순 버튼 표시

1
2
3
4
5
6
7
8
{% for article in articles %}
    <form action="{% url 'articles:like' article.pk %}" method="POST">
        {% csrf_token %}
        <input type="submit" value="좋아요">
    </form>
{% endfor %}

2차 구현: 좋아요 여부에 따라 버튼 변경

1
2
3
4
5
6
7
8
9
10
11
12
{% for article in articles %}
    <form action="{% url 'articles:like' article.pk %}" method="POST">
        {% csrf_token %}
        {% if user in article.like_users.all %}
            <input type="submit" value="좋아요 취소">
        {% else %}
            <input type="submit" value="좋아요">
        {% endif %}
    </form>
{% endfor %}

팔로우 기능 구현하기

1. 팔로우의 정의

  • 팔로우란?: 유저가 다른 유저를 팔로우하여 관계를 형성하는 기능입니다.
  • 저장해야 할 데이터: 어떤 유저가 다른 어떤 유저를 팔로우하는지.

2. 모델 설정

ManyToManyField와 자기참조

1
2
3
4
5
6
7
from django.db import models
from django.contrib.auth.models import AbstractUser

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

설명:

  • symmetrical=False: 대칭 관계가 아닌 경우 설정 (팔로우는 단방향 관계).
  • related_name="followers": 역참조 이름.

3. 팔로우 로직 작성

View 함수 작성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django.shortcuts import get_object_or_404, redirect
from django.views.decorators.http import require_POST
from django.contrib.auth import get_user_model

@require_POST
def follow(request, user_pk):
    if request.user.is_authenticated:
        member = get_object_or_404(get_user_model(), pk=user_pk)
        if request.user != member:
            if request.user in member.followers.all():
                member.followers.remove(request.user)
            else:
                member.followers.add(request.user)
        return redirect("users:profile", member.username)
    return redirect("accounts:login")

URL 설정

1
2
3
4
5
6
7
8
9
from django.urls import path
from . import views

app_name = "users"
urlpatterns = [
    ...
    path("<int:user_id>/follow/", views.follow, name="follow"),
    ...
]

템플릿 작성 1차 구현: 단순 팔로우 버튼 표시

1
2
3
4
5
6
<form action="{% url 'users:follow' member.pk %}" method="POST">
    {% csrf_token %}
    <button type="submit">팔로우</button>
</form>

2차 구현: 팔로우 여부에 따라 버튼 변경

1
2
3
4
5
6
7
8
9
10
<form action="{% url 'users:follow' member.pk %}" method="POST">
    {% csrf_token %}
    {% if user in member.followers.all %}
        <button type="submit">언팔로우</button>
    {% else %}
        <button type="submit">팔로우</button>
    {% endif %}
</form>

결론

Django의 ManyToManyField를 활용하면 다대다 관계를 간단하게 설정하고 관리할 수 있습니다. 좋아요팔로우 같은 기능은 이 필드를 통해 효율적으로 구현할 수 있으며, Django ORM을 이용하면 데이터의 추가, 삭제, 조회가 간단해집니다.

This post is licensed under CC BY 4.0 by the author.