TIL no. 31 Django ManyToManyField

백선호·2021년 7월 20일
1

TIL

목록 보기
28/39
post-thumbnail

View 작성

지금까지는 model만 작성하였지만 view를 작성할 것이다. 보통 view에서는 클라이언트로부터 오는 정보를 받아서 처리를 한다. 만약 http post 요청이 왔다면 http request message body에 테이블의 정보를 Json 형태로 받아서 데이터 베이스에서 지정된 테이블에 저장된다.

Many-to-Many 관계를 가진 Database에 대해 Backend API 구현하는 방법 2가지를 구현해 볼 거다. 첫 번째는 Foreign Key로 중간 테이블을 선언하여 M2M를 구현하는 방법, 두 번째는 ManyToManyField를 사용하면 구현하는 방법이 있다.

Many to Many 관계는 상위 사진과 같다.

목 표

-등록된 배우 목록을 리턴해주는 GET메서드로 배우의 이름, 성, 그리고 출연한 영화 제목 목록을 불러온다.
-등록된 영화 목록을 리턴해주는 GET 메소드로 영화의 제목, 상영시간, 출연한 배우 목록 (이름만)을 불러온다.

1. Foreign Key 방법

-models.py

from django.db import models
from django.db.models.deletion import CASCADE
from django.db.models.fields import TextField, DateField, IntegerField
#actors 테이블
class Actor(models.Model):
    first_name=models.CharField(max_length=45)
    last_name=models.CharField(max_length=45)
    date_of_birth=models.DateField()
    def Meta(self):
        db_table = 'actors'
#movies 테이블
class Movie(models.Model):
    title=models.CharField(max_length=45)
    release_date=models.DateField()
    running_time=models.IntegerField()
    def Meta(self):
        db_table = 'movies'
#중간 테이블
class Actor_movie(models.Model):
    actor=models.ForeignKey('Actor', on_delete=CASCADE)
    movie=models.ForeignKey('Movie', on_delete=CASCADE)
    def Meta(self):
        db_table='actor_movies'```
   상위 코드를 makemigraton 후 migrate 해서 DB에 테이블을 생성한다. 

-views.py

from django.http.response import JsonResponse
from django.views import View
import json
from movies.models import Actor, Movie, Actor_movie, Actor_movie
class ActorView(View):
    def post(self, request):
        data=json.loads(request.body)
        # name=data['first_name']
        # name2=data['last_name']
        # birth=data['data_of_birth']
        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):
        result=[]
        actors=Actor.objects.all()
        for actor in actors:
            movies=[i.movie.title for i in Actor_movie.objects.filter(actor_id=actor.id)]
            result.append({
                'first_name':actor.first_name,
                'last_name': actor.last_name,
                'movie' : movies
            })
        return JsonResponse({'result': result}, status = 200)
class MovieView(View):
    def post(self, request):
        data=json.loads(request.body)
        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):
        result=[]
        movies=Movie.objects.all()
        for movie in movies:
            actors=[i.actor.last_name for i in Actor_movie.objects.filter(movie_id=movie.id)]
            result.append({
                'title' : movie.title,
                'running_time' : movie.running_time,
                'actors' : actors
            })
        return JsonResponse({'result': result}, status = 200)
class Actor_movieView(View):
    def post(self, request):
        data=json.loads(request.body)
        actor_n=data['actor_id']
        movie_n=data['movie_id']
        movie=Movie.objects.get(id=movie_n)
        actor=Actor.objects.get(id=actor_n)
        Actor_movie.objects.create(actor_id=actor.id, movie_id=movie.id)
        return JsonResponse({'message': 'created'}, status=201)
    def get(self, request):
        result=[]
        actor_movies=Actor_movie.objects.all()
        for actor_movie in actor_movies:
            result.append({
                "actor_id" : actor_movie.actor_id,
                "movie_id" : actor_movie.movie_id
            })
        return JsonResponse({'result': result}, status = 200)
     result=[]
     actors=Actor.objects.all()
     for actor in actors:
         movies=[i.movie.title for i in Actor_movie.objects.filter(actor_id=actor.id)]
         result.append({
             'first_name':actor.first_name,
             'last_name': actor.last_name,
             'movie' : movies
         })

get 요청이 왔을 때 actors에 actor에 대한 모든 QuerySet을 가져와 저장한다. 반복문을 돌려서 QuerySet의 하나하나를 반환하는데, 이때 movies라는 리스트에 반복을 돌려 해당 actor가 할당하는 영화를 할당한다. 그 후 result에 딕셔너리 형태로 추가해 준다. 이 부분이 가장 애를 먹인 부분이다.

이런 식으로 중간 테이블을 생성해서 관리해야 할 경우에는 항상 중간 테이블을 거쳐야 관리가 가능하다. 또한 코드가 굉장히 길어지는 단점이 있다.

2. ManyToManyField 방법

-models.py

from django.db import models
from django.db.models.deletion import CASCADE
from django.db.models.fields import TextField, DateField, IntegerField
# Create your models here.
class Actor(models.Model):
    first_name=models.CharField(max_length=45)
    last_name=models.CharField(max_length=45)
    date_of_birth=models.DateField()
    movies =models.ManyToManyField('Movie',through='ActorMovie')
    def Meta(self):
        db_table = 'actors'
class Movie(models.Model):
    title=models.CharField(max_length=45)
    release_date=models.DateField()
    running_time=models.IntegerField()
    def Meta(self):
        db_table = 'movies'

ManyToManyField를 사용하게 되면 중간 테이블을 생성하지 않아도 되며, 두 class를 연결하는 ManyToManyField를 사용하면 된다.

-views.py

from django.views import View
import json
from manyfield.models import Actor, Movie
class ActorView(View):
    def get(self, request):
        result=[]
        actors=Actor.objects.all()
        for actor in actors:
            M=[i.title for i in actor.movies.all()]
            result.append({
                'first_name':actor.first_name,
                'last_name': actor.last_name,
                'movie' : M
            })
        return JsonResponse({'result': result}, status = 200)
class MovieView(View):
    def get(self, request):
        result=[]
        movies=Movie.objects.all()
        for movie in movies:
            A=[i.last_name for i in movie.actors.all()]
            result.append({
                'title' : movie.title,
                'running_time' : movie.running_time,
                'actors' : A
            })
        return JsonResponse({'result': result}, status = 200)
ManyToManyField를 사용할 때 좀 더 간단하게 데이터를 추가 삭제가 가능하다. 예를 들어 actor table에 id=6에 '이서연'이라는 데이터가 있다고 했을 때 출현한 영화를 추가할 수 있다. 코드는 아래와 같다.

Actor.objects.get(id='6').movies.add('5') #6번 배우 5번 영화 출현
Actor.objects.get(id='6').movies.add('4') #6번배우 4번 영화 출현```


이처럼 table을 따로 생성하는 번거로움이 없고 연결된 테이블의 정보를 가져올 경우 훨씬 코드가 간편해진다.

HTTP GET 통신 결과


profile
baik9261@gmail.com

0개의 댓글