djaogo M2M app project

hs·2021년 8월 19일
1

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

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

초기셋팅

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

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개의 댓글