from django.db import models
#배우 테이블 작성(이름, 성, 생일, 출연한 영화(M2M))
class Actor(models.Model):
first_name = models.CharField(max_length = 20)
last_name = models.CharField(max_length = 10)
date_of_birth = models.DateField(auto_now = False, auto_now_add = False) #안에 파라미터는 안적어도 상관 없음
movie = models.ManyToManyField('Movie') #Movie 테이블과 Many To Many 관계, 파라미터로 relative_name 넣어주면 역참조할 때 편함 :)
class Meta:
db_table = 'actors'
class Movie(models.Model):
title = models.CharField(max_length = 80)
release_date = models.DateField()
running_time = IntegerField()
class Meta:
db_table = 'movies'
Actor
클래스에ManyToManyField
를 선언해주어Movie
테이블과 M2M관계가 되었다.
Django에서는ManyToManyField
를 선언해 주면 알아서 중간 테이블(혹은 중계 테이블)이 생성된다. 따라서 따로 class를 만들어 주지 않아도 된다.그러나!
나중에 혹시 중간 테이블에 필드를 추가해야 할 경우가 있으면 수정을 하기 힘들다. 왜냐하면 django에서 자동으로 만들어주는 중계 테이블은 연결된 두 테이블의 각 id만 참조하여 만들어지기 때문이다.
이런 식으로...
Model을 다 정의했으면 migration을 해준다.
python manage.py makemigration
python manage.py migrate
import json
from django.http import JsonResponse
from django.views import View
class ActorView(View):
def get(self, request):
res = []
actors = Actor.objects.all()
for actor in actors:
movie = list(actor.movie.values('title')
res.append(
{
"last_name" : actor.last_name
"first_name" : actor.first_name
"movies" : movie
}
)
return JsonResponse({'results' : res}, status = 200)
class MovieView(View):
def get(self, request):
res = []
movies = Movie.objects.all()
for movie in movies:
actor_res = []
actors = movie.actor_set.all()
for actor in actors:
actor_res.append(
{
"name" : actor.name
}
)
res.append(
{
"movie name" : movie.title
"running time" : movie.running_time
"appearance" : actor_res
}
)
return JsonResponce({'results' : res}, status = 200)
View에서 로직을 다 구현했으면 각 엔드포인트를 등록해 준다.
# main(manage.py가 있는 디렉토리)에서의 urls.py
from django.urls import path, include
urlpatterns = [
path('movies/', include('movies.url')),
]
# app(movies 디렉토리)에서의 urls.py
from django.urls import path
from movies.views import *
urlpatterns = [
path('actor', ActorView.as_view()),
path('movie', MovieView.as_view())
]
처음에 main에 있는 urls.py
를 통해 'movies/'
를 url에 추가한다. 그리고 include
에 있는 파일로 가서 path를 추가한다. 이제 가상의 클라이언트(httpie 유틸리티)를 설치하여 요청을 보내보자.
brew install httpie
설치가 완료되면 요청을 보내보자.
http -v GET 127.0.0.1:8000/movies/actor
일단 main에서 정의한 path인 movies를 통해서 movies.url
을 호출하고 app에서 정의한 path인 actor를 통해 ActorView클래스의 로직을 실행한다. 현재 GET방식으로 요청이 들어왔으므로 get메소드가 실행된다.
ManyToManyField
를 사용하면 자동으로 중계 테이블이 생성된다. 만약ManyToManyField
를 사용하지 않고 따로 중계 테이블을 만들어서ForeignKey
를 사용하여 두 테이블을 연결해 주면 불편함이 증가한다.예를 들어, 배우의 정보가 들어있는 테이블과 영화의 정보가 들어있는 테이블이 있다고 하면 한 배우는 여러 영화에 출현할 수 있다. 따라서 두 테이블은 ManyToMany관계이다.
배우가 출현한 영화의 목록을 확인하기 위해서는 중계테이블을 통해 해당 배우의 id와 매핑된 영화의 id를 찾아서 그 영화 id의 제목을 찾아가야 한다.
그러나ManyToManyField
를 선언하면 배우가 출현한 영화의 제목을 조회하기 위해서는actor.movie.title
이렇게 심플하게 접근할 수 있다.그렇다고 무조건
ManyToManyField
가 좋다는 것은 아니다. 나중에 어떤 배우가 출연한 영화의 감독 필드를 추가하고 싶을 때MTMField
를 선언하면 원하는 필드를 추가하는 것이 힘들다. 따라서 따로 테이블을 정의하는 것도 나쁘지 않은 방법이다.