[Django] ManytoManyField

2021. 8. 26. 20:47코딩일지/Django

1.  ManytoManyField

 

※ AQuerytool을 참조하여 Models.py를 생성

M2M (Many to Many)
Actor와 Movie Class를 만들어낸 models.py

# C.R.U.D (2) 게시물에서는 ForeignKey를 써서 고양이의 주인을 불러왔습니다.

# ※ManytoMany에서도 중간테이블중간 테이블 Class Actor_movie를 만드는 방법도 있지만 M2M은 중간 테이블을 자동 생성되게끔 할 수 있기 때문에 위의 방법을 써보겠습니다.

 

  • Movie 클래스에 M2M field를 주었습니다.
class Movie(models.Model):
        title = models.CharField(max_length=45)
        release_date = models.DateField()
        running_time = models.IntegerField()
        actor = models.ManyToManyField(Actor,related_name="movie")

        class Meta:
            db_table = 'movies'
            
        def __str__(self):
            return self.title

 

 

※ Models.py 작성을 끝내고 makemigrations 및 migrate를 실행해 Database에 적용해 tables을 생성하였습니다.

 # Class를 만들지 않아도 자동으로 movies_actor라는 table이 생성되었습니다.

자동으로 생성된 movies_actor

 

  • 이후 movies폴더 안에 Views.py을 생성합니다.

import json

from django.http import JsonResponse
from django.views import View

from movies.models import Actor, Movie

class ActorsView(View):
    def post(self, request):
        data        = json.loads(request.body)
        actor       = Actor.objects.create(
            first_name      = data['first_name'],
            last_name       = data['last_name'],
            date_of_birth   = data['date_of_birth'],
        )
        return JsonResponse({'MESSAGE':'CREATED'}, status=201)

    def get(self, request):
        actors   = Actor.objects.all()
        results  = []
        for actor in actors:
            # movie_list=[]
            results.append(
                    {
                    "first_name"    : actor.first_name,
                    "last_name"     : actor.last_name,
                    "date_of_birth" : actor.date_of_birth,
                    # "movie_list"    : movie_list,
                    }
                )
            # movies = actor.movie.all()
            # for movie in movies:
            #     movie_list.append(
            #         {
            #         "title"     : movie.title,
            #         }
            #     )
        return JsonResponse({'results':results}, status=200)

class MoviesView(View):
    def post(self, request):
        data        = json.loads(request.body)
        movie       = Movie.objects.create(
            title           = data['title'],
            release_date    = data['release_date'],
            running_time    = data['running_time'],
            )
        return JsonResponse({'MESSAGE':'CREATED'}, status=201)

    def get(self, request):
        actors  = Movie.objects.all()
        results  = []
        for movie in actors:
            ★# actor_list = []
            results.append(
                {
                    "title"         : movie.title,
                    "release_date"  : movie.release_date,
                    "running_time"  : movie.running_time,
                    ★# "actor_list" : actor_list,
                }
            )
            # actors = movie.actor.all()
            # for actor in actors:
            #     actor_list.append(
            #         {
            #         "first_name"    : actor.first_name,
            #         }
            #     )
        return JsonResponse({'results':results}, status=200)

★ 주석처리 한부분은 M2M으로 서로 불러주기 위해서 기입한 내용입니다. (값을 기입하는 POST에서 설명드리겠습니다.)

 복문 처음에 actor_list = []로 초기화를 주었는데 값이 제대로 나온것은 아래의 actor_list가 코드를 복사한것이 아니라 객체의 주소를 바라보게 하기 때문에 정상적으로 함수가 돌아가게 되는것이다.(얕은 복사)

 

# 얕은 복사와 깊은 복사에 대해 더 알고 싶다면

출처 : https://velog.io/@chs_0303/%EC%96%95% EC% 9D%80-%EB% B3% B5% EC%82% ACshallow-copy% EC%99%80-%EA% B9% 8A% EC% 9D%80-%EB% B3% B5% EC%82% ACdeep-copy

 

※ View 를 작성한 후에는, 클라이언트의 요청을 받아 적절한 view를 맵핑해주는 urls.py를 작성해주어야 합니다.

  • movies 디렉토리 안에 urls.py를 만들어줍니다.
touch urls.py
vi urls.py

 

  • products/urls.py 작성
from django.urls import path

from movies.views import MoviesView, ActorsView

urlpatterns = [
	path('/movies', MoviesView.as_view()),
    path('/actors', ActorsView.as_view()),
]

 

  • 처음 클라이언트로 부터 요청을 받기 위한 movie> urls.py(movies/urls.py)와 연결해주어야 합니다.
from django.urls import path, include

urlpatterns = [
    path('movies', include('movies.urls'))
]

 

# <Python shell, http -v POST>두가지 방법으로 데이터에 내용을 채워줄 수 있습니다.

# Views.py를 다 만들었기에 POST를 사용하여 데이터를 넣어주겠습니다.

  • Runserver를 입력하여 서버를 가동합니다.
python manage.py runserver

python manage.py runserver

http -v POST 127.0.0.1:8000/movies/movies title='영화명' release_date=0000-00-00 running_time=000
  • POST로 넣어주어서 성공하면 "MESSAGE" : "CREATED" 메세지가 뜨게 됩니다.

 

  • '타짜' 영화의 배우진도 POST를 사용하여 넣어주겠습니다.

 

# 데이터가 잘 들어갔는지 확인하는 방법으로 httpie GET을 통해서 확인하는 방법과 mysql 데이터 테이블을 확인하는 방법이 있습니다. 

  • mysql을 이용하여 데이터 확인

  • http GET을 이용하여 데이터 확인

★ views.py에서 #주석 처리했던 actor_list, movie_list는 ManytoMany를 참조하여 중간 테이블에 id값을 불러와서 서로를 부를 수 있게 만들어줍니다. (주석을 지우면 이렇게 표현됩니다.)

 

※ python manage.py shell와 related_name을 이용해 M2M(Actor와 movie를 연결시켜주겠습니다)

  • python manage.py shell 실행 및 import 해주기



  • get을 이용해 id값을 불러와서 Actor와 Movie를 넣어주고 확인합니다.

a1,a2,a3,m1에 Get함수를 이용해 아이디값으로 넣어준다.
related_name="movie", add를 이용해 중간테이블에 연결

☆ m1.actor.add(a1, a2, a3)로 한꺼번에 넣어주는 방법도 있다.

movies_actor table에 제대로 들어간것을 확인

  • httpie GET을 이용해 Actor 호출

중간테이블에 연결된 영화 제목들이 딸려서 출력된모습이 보인다.

 중간 테이블을 만들어서 연결해주는 방법도 있지만 ManyToMany를 사용할때의 장점은?

중간 테이블로 연결된 Actor table과 Movie table이 있다면 A에서 M로 접근하고자 할때 중간테이블을 거치지 않고 접근이 가능하다 (역으로 M에서 A로도 할 수 있다. )

 

# 더 많은 ManytoMany add()에 대한 정보 (Django 공식문서)

출처 : https://docs.djangoproject.com/en/3.2/topics/db/examples/many_to_many/

 

'코딩일지 > Django' 카테고리의 다른 글

[Django] Pagination과 offset, limit  (0) 2021.09.05
[Djagno] Westagram 회원가입 기능 구현  (0) 2021.08.29
[Django]C.R.U.D (3)  (0) 2021.08.26
[Django] C.R.U.D (2)  (0) 2021.08.21
[Django] C.R.U.D (1)  (0) 2021.08.19