배우와 영화는 M2M 관계이다. 모델링을 할 때 2가지 방법으로 작성할 수 있다.
1. 중간테이블을 사용한 관계
2. ManyToManyFiled를 사용한 관계 (중간테이블 없음)
from django.db import models
class Actor(models.Model):
first_name = models.CharField(max_length=45)
last_name = models.CharField(max_length=45)
birth = models.DateField()
class Meta:
db_table = "actors"
class Movie(models.Model):
title = models.CharField(max_length=45)
release_date = models.DateField()
running_time = models.IntegerField()
class Meta:
db_table = "movies"
class ActorMovie(models.Model):
actor = models.ForeignKey("Actor", on_delete=models.CASCADE)
movie = models.ForeignKey("Movie", on_delete=models.CASCADE)
class Meta:
db_table = "actors_movies"
배우와 영화 테이블, 중간테이블을 생성한다. 중간테이블의 ForeignKey
로 둘의 관계를 연결해준다. 모델 메소드를 사용해서 데이터를 생성한다. 이 때 중간테이블에 actor_id
와 movie_id
의 값을 직접 연결시켜줘야 한다.
import json
from django.http import JsonResponse
from django.views import View
from movies2.models import Actor, Movie
class ActorView(View):
def get(self, request):
actors = Actor.objects.all()
results = [
{
"first_name" : actor.first_name,
"last_name" : actor.last_name,
"title" : [actor_movie.movie.title for actor_movie in actor.actormovie_set.all()]
}
for actor in actors
]
return JsonResponse({"actors" : results}, status=200)
class MovieView(View):
def get(self, request):
movies = Movie.objects.all()
results=[
{
"title" : movie.title,
"release_date" : movie.release_date,
"actor" : [movie_actor.actor.first_name for movie_actor in movie.actormovie_set.all()]
}
for movie in movies
]
return JsonResponse({"movies":results}, status=200)
배우 테이블에서 조회할 데이터는 1. 배우의 이름과 2. 출연한 영화 제목이다. 배우 테이블에서 영화를 어떻게 연결시키는 것일지 살펴보자.
테이블의 관계를 살펴보면 배우 테이블과 영화 테이블은 중간테이블을 역참조한다. 중간테이블은 두 테이블을 각각 정참조한다.
배우 <- 중간테이블
영화 <- 중간테이블
정참조 :참조하는 테이블.참조되는 테이블.필드명
역참조 :참조되는 테이블.참조하는 테이블_set.all()
먼저 배우와 중간테이블의 관계에 대해 아래 예제를 보며 살펴보자.
ActorMovie.objects.get(id=1).actor.id
로 바로 연결할 수 있다. actormovies = Actor.objects.get(id=1).actormovie_set.all()
: 배우 id=1에 연결된 중간테이블 row는 여러 개이다. all
메소드로 모두 불러오는데 쿼리셋 형식으로 반환하기 때문에, json이 이해할 수 있는 리스트로 바꿔준다.[actormovie.id for actormovie in actormovies]
: 중간테이블 id들을 하나씩 리스트에 넣어준다. 이제 중간테이블을 거쳐 출연한 영화를 연결지어보자.
중간테이블은 영화를 정참조하므로 중간테이블.영화테이블.영화필드명
이 된다.
배우-중간테이블-영화를 합쳐서 표현해보자.
actor_movies = actor.actormovie_set.all()
: 한 배우가 역참조하는 중간테이블 모든 row를 가져온다.
[actor_movie.movie.title for actor_movie in actor_movies]
: 중간테이블 각각의 row가 정참조하는 영화의 제목을 가져온다.
from django.db import models
class Actor(models.Model):
first_name = models.CharField(max_length=45)
last_name = models.CharField(max_length=45)
birth = models.DateField()
class Meta:
db_table = "actor"
class Movie(models.Model):
title = models.CharField(max_length=45)
release_date = models.DateField()
run_time = models.IntegerField()
actor = models.ManyToManyField('Actor')
class Meta:
db_table = "movie"
배우와 영화 테이블 한 쪽에 ManyToManyField
를 사용하고 관계되는 테이블 이름을 써준다. 중간테이블을 생성하지 않았지만 MySQL에 접속하면 자동으로 생성되어 있다.
영화 테이블에서 ManyToManyField
를 넣어주었다. 영화는 배우를 정참조하고, 배우는 영화를 역참조하는 관계이다.
MnayToManyField
를 사용하면서 중간테이블을 생성해주어도 괜찮다. 중간테이블이 확장할 가능성이 있다면 직접 만들어서 관리해주는 것도 좋다.
import json
from django.http import JsonResponse
from django.views import View
from movies.models import Actor, Movie
class ActorView(View):
def get(self, request):
actors = Actor.objects.all()
results = []
for actor in actors:
movies = actor.movie_set.all()
results.append(
{
"first_name" : actor.first_name,
"last_name" : actor.last_name,
"title" : [movie.title for movie in movies]
}
)
return JsonResponse({"result":results}, status=200)
class MovieView(View):
def get(self,request):
movies = Movie.objects.all()
results = []
for movie in movies:
actors = movie.actor.all()
results.append(
{
"title" : movie.title,
"release_date" : movie.release_date,
"run_time" : movie.run_time,
"actor" : [actor.first_name for actor in actors]
}
)
return JsonResponse({"result":results}, status=200)
배우가 출연한 영화들을 조회해보자.
배우는 영화를 역참조하는 관계이므로 actor.movie_set.all()
로 작성한다.
영화에 나온 배우들을 조회해보자.
영화는 배우를 정참조하는 관계이므로 _set
을 생략하고 movie.actor.all()
로 작성한다.