actors-movies 테이블 사이 이런 관계가 있을 때
ForeignKey 사용
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()
class Meta:
db_table = 'movies'
class ActorMovie(models.Model):
movie = models.ForeignKey('Movie', on_delete=models.CASCADE)
actor = models.ForeignKey('Actor', on_delete=models.CASCADE)
class Meta:
db_table = 'actors_movies'
관계테이블을 따로 정의하고 두 테이블이랑 일대다 관계로 각각 이어줘야 한다.
ManyToManyField
장고에서 제공하는 ManyToManyField를 이용하면 관계테이블을 직접 만들지 않고 간단하게 작성할 수 있다.
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()
movies = models.ManyToManyField('Movie') # 이 속성은 데이터베이스에 생기지 않는다 movies는 참조를 위한 매니저 클래스가 된다
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'
데이터베이스에도 자동으로 <m2m관계를 적어준 테이블이름>_<다대다관계인 테이블이름>으로 관계 테이블이 생성된다
mysql> show tables;
+---------------------+
| Tables_in_crud2 |
+---------------------+
| actors |
| actors_movies |
| django_content_type |
| django_migrations |
| django_session |
| movies |
+---------------------+
Actor에 준 movies라는 속성은 데이터베이스에 컬럼을 만들지 않는다.
외래키만 가지고 다대다 관계를 만들면 Actor->Movie, Movie->Actor에 접근하려면 관계 테이블을 통해 접근해야 한다.
Movie.objects.filter(movieactor__actor=actor)
그런데 장고의 매니투매니필드를 이용하면 movies라는 속성이 many to many관계에서 매니저클래스가 된다. 그래서 중간 테이블을 타고 들어가지 않아도 서로에게 접근이 가능하다.
Actor에서는 movies를 통해서(정참조),
Movie에서는 actor_set(역참조)를 통해서 관계있는 테이블에 접근이 가능하다.
Actor.movies.all()
Movie.actor_set.all()
역참조에서 매니저클래스는 상대클래스 이름의 소문자_set으로 자동으로 정해지는데
그게 싫으면 related_name이라는 속성을 주면 된다.
movies = models.ManyToManyField('Movie', related_name='actors')
이렇게 하면 Movie에서 Actor객체에 접근할때 Movie.actors.get.....이런식으로 접근이 가능하다
관계 만들기
테이블에 넣은 데이터는 아래와 같다.
mysql> table movies;
+----+---------------------------------+--------------+--------------+
| id | title | release_date | running_time |
+----+---------------------------------+--------------+--------------+
| 1 | 장고: 분노의 추적자 | 2013-03-21 | 165 |
| 2 | 바스터즈: 거친 녀석들 | 2009-10-29 | 153 |
| 3 | 토이스토리 | 1995-12-23 | 81 |
| 4 | 위대한 개츠비 | 2013-05-16 | 142 |
| 7 | 돈 룩 업 | 2021-12-08 | 145 |
| 8 | 헝거 게임: 판엠의 불꽃 | 2012-04-05 | 142 |
| 9 | 레이디 버드 | 2017-11-10 | 94 |
| 10 | 작은 아씨들 | 2020-02-12 | 135 |
+----+---------------------------------+--------------+--------------+
8 rows in set (0.00 sec)
mysql> table actors;
+----+-----------------+-----------------+---------------+
| id | first_name | last_name | date_of_birth |
+----+-----------------+-----------------+---------------+
| 1 | 톰 | 행크스 | 1956-07-09 |
| 2 | Christoph | Waltz | 1956-10-04 |
| 3 | 브래드 | 피트 | 1963-12-18 |
| 4 | 레오나르도 | 디카프리오 | 1974-11-11 |
| 5 | Carey | Mulligan | 1985-05-28 |
| 7 | 제니퍼 | 로렌스 | 1990-08-15 |
| 8 | 티모시 | 샬라메 | 1995-12-27 |
| 9 | 플로렌스 | 퓨 | 1996-01-03 |
| 10 | Saoirse | Ronan | 1994-04-12 |
+----+-----------------+-----------------+---------------+
9 rows in set (0.00 sec)
둘을 관계 지을때는 객체를 이용한다.
정참조
>>> toys = Movie.objects.get(id=3)
>>> tomh = Actor.objects.get(id=1)
>>> tomh.movies.add(toys)
actors의 id 1인 톰 행크스와 movies에서 id=3인 토이스토리의 관계를 만들어주었다.
# 결과:
mysql> table actors_movies;
+----+----------+----------+
| id | actor_id | movie_id |
+----+----------+----------+
| 1 | 1 | 3 |
+----+----------+----------+
나머지도 관계를 설정해줬다.
>>> a2=Actor.objects.get(id=2)
>>> movies=Movie.objects.all()
>>> movies[0]
<Movie: Movie object (1)>
>>> a2.movies.add(movies[0],movies[1])
>>> a=Actor.objects.all()
>>> a[2].movies.add(movies[1])
>>> a[3].movies.add(movies[0], movies[3]) #여러개일때는 ,로 구분해서 한번에 가능
>>> a[4].movies.add(movies[3])
# 결과
mysql> table actors_movies;
+----+----------+----------+
| id | actor_id | movie_id |
+----+----------+----------+
| 1 | 1 | 3 |
| 2 | 2 | 1 |
| 3 | 2 | 2 |
| 4 | 3 | 2 |
| 5 | 4 | 1 |
| 6 | 4 | 4 |
| 7 | 5 | 4 |
+----+----------+----------+
7 rows in set (0.00 sec)
역참조
Movie클래스에서 actor_set을 이용해서 역참조로 관계를 설정하기
In [23]: dlu=Movie.objects.get(id=7)
In [24]: actors=Actor.objects.all()
In [25]: dlu.actor_set.add(actors[3], actors[5], actors[6])
# 결과
mysql> table actors_movies;
+----+----------+----------+
| id | actor_id | movie_id |
+----+----------+----------+
| 1 | 1 | 3 |
| 2 | 2 | 1 |
| 3 | 2 | 2 |
| 4 | 3 | 2 |
| 5 | 4 | 1 |
| 6 | 4 | 4 |
| 9 | 4 | 7 |
| 7 | 5 | 4 |
| 10 | 7 | 7 |
| 8 | 8 | 7 |
+----+----------+----------+
10 rows in set (0.00 sec)
In [27]: Actor.objects.get(id=7).movies.add(Movie.objects.get(id=8))
In [28]: Movie.objects.get(id=9).actor_set.add(actors[6],actors[8])
In [29]: Movie.objects.get(id=10).actor_set.add(actors[6],actors[7],actors[8])
# 결과
mysql> table actors_movies;
+----+----------+----------+
| id | actor_id | movie_id |
+----+----------+----------+
| 1 | 1 | 3 |
| 2 | 2 | 1 |
| 3 | 2 | 2 |
| 4 | 3 | 2 |
| 5 | 4 | 1 |
| 6 | 4 | 4 |
| 9 | 4 | 7 |
| 7 | 5 | 4 |
| 10 | 7 | 7 |
| 11 | 7 | 8 |
| 8 | 8 | 7 |
| 12 | 8 | 9 |
| 14 | 8 | 10 |
| 15 | 9 | 10 |
| 13 | 10 | 9 |
| 16 | 10 | 10 |
+----+----------+----------+
16 rows in set (0.00 sec)
관계를 이용해서 데이터 불러오기
정참조
Actor->Movie를 참조할땐 매니저클래스 movies를 사용할 수 있다.
In [30]: a1=Actor.objects.get(first_name__contains='모시')
In [31]: m1=[movie.title for movie in a1.movies.all()]
In [32]: m1
Out[32]: ['돈 룩 업', '레이디 버드', '작은 아씨들']
역참조
ManyToManyField가 정의돼있지 않은 Movie 클래스에서 Actor 클래스를 참조할 때는 역참조이다.
actor_set을 이용해서 참조할 수 있다.
In [11]: movie1=Movie.objects.get(title__startswith='장고')
In [12]: movie1.title
Out[12]: '장고: 분노의 추적자'
In [13]: actors1=movie1.actor_set.all()
In [14]: for actor in actors1:
...: print(actor.first_name + actor.last_name)
...:
ChristophWaltz
레오나르도디카프리오
참고자료
https://docs.djangoproject.com/en/4.0/topics/db/examples/many_to_many/
'Django' 카테고리의 다른 글
Django | models : verbose_name (0) | 2022.08.20 |
---|---|
Django | ORM : on_delete=models.CASCADE (0) | 2022.08.14 |
Django | 프로젝트 초기세팅 (0) | 2022.07.16 |
Django | 파이썬 쉘에서 db에 있는 데이터 id값 바꾸기 (0) | 2022.07.06 |
Django | sql - 장고 모델 속성의 데이터타입 (0) | 2022.07.05 |