Django/Django Vanila

12. Many-To-Many 모델 생성(related_name)

S.T.Lee 2022. 5. 30. 13:28
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.conf import settings #projects/settings

class UserModel(AbstractUser):
    class Meta:
        db_table = "my_user"

    bio = models.CharField(max_length=256, default='')
    follow = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='followee')

다시 makemigrations, migrate를 해주고 원할한 비교를 위해 6명의 회원을 추가로 가입해준다.

이후, admin페이지에서 전에 토핑을 추가했던거처럼 팔로워를 추가해준다.

related_name은 무엇일까? 하단의 코드를 보면 related_name인 followee를 활용 데이터를 뽑아옴을 알 수 있다.

# user/views.py 
@login_required
def user_view(request):
    if request.method == 'GET':
        # 사용자를 불러오기, exclude와 request.user.username 를 사용해서 '로그인 한 사용자'를 제외하기
        user_list = UserModel.objects.all().exclude(username=request.user.username)
        return render(request, 'user/user_list.html', {'user_list': user_list})


@login_required
def user_follow(request, id):
    me = request.user
    click_user = UserModel.objects.get(id=id)
    if me in click_user.followee.all():
        click_user.followee.remove(request.user)
    else:
        click_user.followee.add(request.user)
    return redirect('/user')

이는 related_name이 User모델을 역관계에 있는 모델에서 부를 때 사용된다.

class UserModel(AbstractUser):
	''''''
    follow = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='followee')
#우리는 Django 모델을 참조하여 UserModel안에 follow를 만들었다.

click_user = UserModel.objects.get(id=id)
if me in click_user.followee.all():
    click_user.followee.remove(request.user)
#이후 click_user는 UserModel를 불러왔고
#UserModel에서 Django User를 참조하는 상황이 나왔다.
#따라서 Django User의 데이터를 받아오기 위해서는 related_name인 followee를 사용해야한다.

참고로 related_name을 따로 설정하지 않으면 (모델이름)_set으로 설정된다.

원래는 related_name을 설정을 해줘도 (모델이름)_set으로 불러와졌는데 Django 2.1.3 언저리부터 안된다고 한다.

좀 더 확인을 해보면

>>> from user.models import UserModel
>>> UserModel.objects.all()
<QuerySet [<UserModel: user>, <UserModel: 1>, <UserModel: 2>, <UserModel: 3>, <UserModel: 4>, <UserModel: 5>, <UserModel: 6>]>
>>> UserModel.objects.get(username = '1')  
<UserModel: 1>
>>> UserModel.objects.get(username = '1').followee.all()
<QuerySet []>
>>> UserModel.objects.get(username = '6').followee.all() 
<QuerySet [<UserModel: 1>]>
>>> UserModel.objects.get(username = '6').follow_set.all() 
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'UserModel' object has no attribute 'follow_set'
>>> UserModel.objects.get(username = '6').follow.all()     
<QuerySet []>

위를 통해 follow와 followee의 차이를 볼 수 있다. 문자 그대로 follow는 자신(UserModel)이 저장한 데이터, followee는 Django User(결국 UserModel이 참조하였기에 같은 데이터다)에서 가져오는 데이터이다.

 

이제 user/urls.py를 수정해준다.

path('user/', views.user_view, name='user-list'),
path('user/follow/<int:id>/', views.user_follow, name='user-follow')

다음 temlats/user/user_list.html을 추가해준다.

{% extends 'base.html' %}
{% block title %}
    사용자 리스트
{% endblock %}

{% block content %}
    <div class="container timeline-container">
        <div class="row">
            <!-- 왼쪽 컬럼 -->
            <div class="col-md-3">
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title">{{ user.username }}</h5>
                        <p class="card-text"> {{ user.bio }}</p>

                    </div>
                </div>
            </div>
            <!-- 오른 쪽 컬럼-->
            <div class="col-md-7">
                <div class="row">
                    <div class="alert alert-success" role="alert">
                        나를 팔로우 하는 사람 수 : {{ user.followee.count }} 명 / 내가 팔로우 하는 사람 수 : {{ user.follow.count }} 명
                    </div>
                </div>
                <div class="row">
                    <!-- 사용자 리스트 반복문 -->
                    {% for ul in user_list %}
                        <div class="card">
                            <div class="card-body">
                                <h5 class="card-title">{{ ul.username }}</h5>
                                <h6 class="card-subtitle mb-2 text-muted">{{ ul.email }}</h6>
                                <p class="card-text">
                                    {{ ul.bio }}
                                </p>
                                <p class="card-text">
                                    팔로잉 {{ ul.follow.count }} 명 / 팔로워 {{ ul.followee.count }} 명
                                </p>
                                {% if ul in user.follow.all %}
                                    <a href="/user/follow/{{ ul.id }}" class="card-link">[팔로우 취소]</a>
                                {% else %}
                                    <a href="/user/follow/{{ ul.id }}" class="card-link">[팔로우]</a>
                                {% endif %}
                            </div>
                        </div>
                        <hr>
                    {% endfor %}
                </div>
            </div>
            <div class="col-md-2"></div>
        </div>
    </div>
{% endblock %}

이후 templates/base.html에서 수정을 해준다.

<a class="nav-link" href="/user"> 친구 <span class="sr-only"></span></a>

그럼 완료