djaogo M2M app project

hs·2021년 8월 19일

초기 셋팅부터 처음부터 차근차근 해보며 오류가 나는 부분이나 모르는 부분에 대해 정리해가며 포스팅해 나갈 예정입니다.

잘못된 부분이 있으면 지적해주시면 감사하겠습니다!

초기셋팅

가상환경을 생성하고 접속한 뒤 필요한 툴이나 패키지를 다운 받는다.

conda create -u Movie python=3.9
conda activate Movie
...
pip install django
pip install mysql-client
pip install django-cors-headers
pip install PyMySQL

프로젝트 생성

django-admin startproject movie

settings.py파일과 urls.py파일 수정

데이터베이스 생성

mysql -u root -p
mysql> create database M2Mmovie character set utf8mb4 collate utf8mb4_general_ci;

실행 오류 검증

python manage.py runserver

시작

이러한 테이블을 만들어보자.

python manage.py startapp movies

앱 생성 후,

# movies/models.py
from django.db import models

class Actor(models.Model):
    first_name=models.CharField(max_length=45)
    last_name=models.CharField(max_length=45)
    date_of_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()
    actor = models.ManyToManyField('Actor', through='ActorMovie')

    class Meta:
        db_table='movies'

Movie class에 ManyToManyField()를 사용하여 연결한다.
그 후 , models.py에 작성한 코드를 database에 적용하기 위해서 migration파일을 생성한다.

python manage.py makemigrations movies(app이름)

다음으로 migrations파일을 database에 적용

python manage.py migrate

이후 mysql에 들어가 데이터베이스에서 테이블을 조회하면 다음과 같이 나올 것입니다.mysql> show tables;

+---------------------+
| Tables_in_m2mmovie  |
+---------------------+
| actors              |
| django_content_type |
| django_migrations   |
| django_session      |
| movies              |
| movies_actor        |
+---------------------+
6 rows in set (0.00 sec)

데이터 삽입

python manage.py shell

shell에 접속한 뒤 Actor,Movie를 import해와 데이터를 넣어준다.

In [1]: from movies.models import *
In [2]: import datetime
In [3]: d = datetime.date(2020, 2, 10)
In [4]: Actor.objects.create(first_name = '강호', last_name = '송', date_of_birth = d)
Out[4]: <Actor: Actor object (1)>
...

대략 이러한 테이블들이 만들어진다.

mysql> select * from movies;
+----+-----------------+--------------+--------------+
| id | title           | release_date | running_time |
+----+-----------------+--------------+--------------+
|  1 | 택시운전사      | 2017-08-02   |          137 |
|  2 | 타짜            | 2006-09-28   |          139 |
|  3 | 변호인          | 2013-12-18   |          127 |
|  4 | 암살            | 2015-07-22   |          139 |
|  5 | 베테랑          | 2015-08-05   |          123 |
+----+-----------------+--------------+--------------+
mysql> select * from actors;
+----+------------+-----------+---------------+
| id | first_name | last_name | date_of_birth |
+----+------------+-----------+---------------+
|  1 | 강호       | 송        | 2020-02-10    |
|  2 | 승우       | 조        | 1980-03-17    |
|  3 | 해진       | 유        | 1970-01-04    |
|  4 | 달수       | 오        | 1968-06-15    |
+----+------------+-----------+---------------+

다음 문제는 이제 두 테이블을 연결해주는 manyrelatedmanager 테이블에 값을 어떻게 넣어주냐이다.
방법은 Movie class의 manytomanyfield를 넣어준 actor가 해답이였다.

In [8]: m1 = Movie.objects.get(id=1)
In [9]: m1.actor.add(4)

한 변수에 Movie의 한 객체를 가져온 뒤 그 객체의 actor와 Actor테이블의 id값과 매칭을 시켜주는 것이였다.

View 파일 작성

이 테이블에서 데이터를 가져오는 GET을 목표로 작성해보겠다.

import json

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

from movies.models import Movie, Actor

class ActorView(View):
    def get(self, request):
        actors = Actor.objects.all()
        result=[]
        for actor in actors:
            result.append(
                {
                    "first_name" : actor.first_name,
                    "last_name" : actor.last_name,
                    "movie" : list(actor.movie_set.values("title"))
                }
            )
        return JsonResponse({'result': result},status=200)

class MovieView(View):
    def get(self, request):
        movies = Movie.objects.all()
        result=[]
        for movie in movies:
            result.append(
                {
                    "title" : movie.title,
                    "running_time" : movie.running_time,
                    "first_name" : list(movie.actor.values("first_name"))
                }
            )
        return JsonResponse({'result': result},status=200)

일단 각 테이블의 모든 객체를 가져와 각각 actors, movies에 저장을 한 후 빈 리스트하나를 선언한 뒤 반복문을 돌려 append()로 딕셔너리 형태로 추가한다. 그 안에 actor나 movie의 인자들은 일반적인 방법으로 넣는 것이 문제는 없었다. 하지만 M2M table을 통해 데이터 값을 가져오는 것은 생각이 많이 필요했다. 내가 찾아낸 방법은 models.py에서 manytomanyfield를 actor로 받아왔으므로 이를 사용할 수 있겠다 생각했다. 반복문 한번에 movie의 한 객체씩 받아오므로 movie.actor 로 연결된 Queryset을 가져온 후 .values("title")로 title에 관한 값들만 가져와 출력한다. 반대로 actor의 경우는 _set 을 사용하여 역 참조하여 값을 가져왔다.

urls.py 작성

클라이언트의 요청을 받아 적절한 view를 맵핑해주는 urls.py를 작성한다.

from django.urls import path, include

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

.as_view()는 클래스형 뷰를 작성했기 때문에 사용해야한다.

main의 urls.py와 연결을 해주기 위해 앱 내에 새로운 urls.py를 작성한다.

from django.urls import path

from movies.views import ActorView, MovieView

urlpatterns = [
    path('actor', ActorView.as_view()),
    path('movie', MovieView.as_view()),
]

위와 같이 작성하면 2가지의 url이 가능한 것이다.(movies/actor, movies/movie)

출력

=======================================
http -v GET 127.0.0.1:8000/movies/actor
=======================================
{
    "result": [
        {
            "first_name": "강호",
            "last_name": "송",
            "movie": [
                {
                    "title": "택시운전사"
                },
                {
                    "title": "변호인"
                }
            ]
        },
        {
            "first_name": "승우",
            "last_name": "조",
            "movie": [
                {
                    "title": "타짜"
                },
                {
                    "title": "암살"
                }
            ]
        },
 ...
=======================================
http -v GET 127.0.0.1:8000/movies/movie
=======================================
{
    "result": [
        {
            "first_name": [
                {
                    "first_name": "강호"
                },
                {
                    "first_name": "해진"
                },
                {
                    "first_name": "달수"
                }
            ],
            "running_time": 137,
            "title": "택시운전사"
        },
        {
            "first_name": [
                {
                    "first_name": "승우"
                },
                {
                    "first_name": "해진"
                }
            ],
            "running_time": 139,
            "title": "타짜"
        },
...

아쉬운 점

model.py부터 보면 테이블을 생성할 때부터 같은 app안에 같이 생성을 해버리니 데이터 호출도 같은 곳에서 하게되고 url도 구분이 잘되진 않았다. 처음에 선언을 다른 app으로 해서 만든 후에 manytomany로 연결 할 수 있다는 것을 후에 알게 되었다.

profile
무엇이든 끝까지 보람차게

0개의 댓글