python/Django
[Django] ORM - N:N 관계 (ManytoManyField) (다대다관계 1)
http://portfolio.wonpaper.net
2022. 5. 14. 10:50
이번에는 모델들이 N:N 관계로 다대다 관계로 연결될 경우이다.
아래와 같이 Album 과 Publication 모델이 다대다 관계라고 설정해 보자.
1
2
3
4
5
6
7
8
9
|
class Album(models.Model):
name = models.CharField('NAME', max_length=30)
description = models.CharField('One Line Description', max_length=100, blank=True)
owner = models.ForeignKey('auth.User', on_delete=models.CASCADE, verbose_name='OWNER', blank=True, null=True)
class Publication(models.Model):
title = models.CharField(max_lendth=30)
albums = models.ManyToManyField(Album)
|
cs |
[ N:N 관계]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
>>> from photo.models import Album, Publication
# 출판물 객체3개를 임의로 만들고 DB에 저장한다.
>>> p1 = Publication(title='The Python Journal')
>>> p1.save()
>>> p2 = Publication(title='Science News')
>>> p2.save()
>>> p3 = Publication(title='Science Weekly')
>>> p3.save()
# 출판물 전체 객체 확인
>>> Publication.objects.all()
<QuerySet [<Publication: publication object (1)>, <Publication: publication object (2)>, <Publication: publication object (3)>]>
# 앨범 객체 전체 리스트
>>> Album.objects.all()
<QuerySet [<Album: Django>, <Album: Nature>, <Album: TestAlbum1>, <Album: TestAlbum2>, <Album: 국가별>, <Album: 사람들>]>
# 테이블에 있는 앨범 객체 하나 조회한다.
>>> a1 = Album.objects.get(name='Django')
# 출판물 p1에 앨범 a1을 연결한다.
>>> p1.albums.add(a1)
# 출판물 p1에 게시된 모든 앨범 리스트를 확인한다.
>>> p1.albums.all()
<QuerySet [<Album: Django>]>
# 반대 방향으로, 앨범 a1이 게시된 모든 출판물 리스트를 확인한다.
>>> a1.publication_set.all()
<QuerySet [<Publication: Publication object (1)>]>
# 모델 간 관계에서도 다양한 필드 검색이 가능하다.
>>> Publication.objects.filter(albums=a1)
<QuerySet [<Publication: Publication object (1)>]>
>>> Publication.objects.filter(albums__pk=1)
<QuerySet [<Publication: Publication object (1)>]>
>>> Publication.objects.filter(albums__id=1)
<QuerySet [<Publication: Publication object (1)>]>
>>> Publication.objects.filter(albums=1)
<QuerySet [<Publication: Publication object (1)>]>
>>> Publication.objects.filter(albums__name__startswidth='Django')
<QuerySet [<Publication: Publication object (1)>]>
>>> Publication.objects.filter(albums__in=[a1])
<QuerySet [<Publication: Publication object (1)>]>
>>> Publication.objects.filter(albums__name__startswidth='Django').count()
1
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
# 반대 방향으로도 필드 검색이 가능하다.
>>> Album.objects.filter(publication=p1)
<QuerySet [<Album: Django>]>
>>> Album.objects.filter(publication=1)
<QuerySet [<Album: Django>]>
>>> Album.objects.filter(publication__title__startswidth='The')
<QuerySet [<Album: Django>]>
>>> Album.objects.filter(publication__in=[p1])
<QuerySet [<Album: Django>]>
# 모델 간 관계에서도 filter() 와 마찬가지로 exclude() 가 가능하다.
>>> Publication.objects.exclude(albums=a1)
<QuerySet [<Publication: Publication object (2)>, <Publication: Publication object (3)>]>
# [삭제1] 실습을 위해 앨범 a2 와 출판물 p2 간에 관계를 연결한다.
>>> a2 = Album.objects.get(name='TestAlbum2')
>>> a2.publication_set.add(p2)
>>> a2.publication_set.all()
<QuerySet [<Publication: Publication objects (2)>]>
>>> p2.albums.all()
<QuerySet [<Album: TestAlbum2>]
# 앨범을 삭제한다. 2개의 레코드가 삭제된다.
>>> a2.delete()
(2, {'photo.Photo': 0, 'photo.Publication_albums': 1, 'photo.Album': 1})
# 앨범이 삭제된걸 확인한다.
>>> Album.objects.all()
<QuerySet [<Album: Django>, <Album: Nature>, <Album: TestAlbum1>, <Album: 국가별>, <Album: 사람들>]>
# 그런데 주의할점은 p2 출판물은 삭제가 되지 않는다는 것이다.
# (FoeignKey의 CASCADE 와는 다르다.)
>>> Publication.objects.all()
<QuerySet [<Publication: Publication object (1)>, <Publication: Publication object (2)>, <Publication: Publication object (3)>]>
# 삭제된 a2로 연결된 Publication 확인해도 연결이 끊어져서 조회가 안된다.
>>> a2.publication_set.all()
Trackback (most recent call last):
File "<console>", line 1, in <module>
File "/home/......related.py", line 527, in __get___
....
ValueError: "<Album: TestAlbum2>" needs to have a value filed "id" before this many-to-many relationship can be used.
# p2 출판물에 연결된 앨범이 없다.
>>> p2.albums.all()
<QuerySet []>
|
cs |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
# [삭제2] 실습을 위해 앨범 a3 과 출판물 p3 간에 연결시킨다.
>>> a3 = Album.objects.get(name='TestAlbum1')
>>> p3.albums.add(a3)
# a3.publication_set.add(p3) 와 동일
>>> p3.albums.all()
<QuerySet [<Album: TestAlbum1>]>
>>> a3.publication_set.all()
<QuerySet [<Publication: Publication object (3)>]>
# 이번에는 출판물 쪽에서 삭제한다. 2개의 레코드가 삭제된다.
>>> p3.delete()
(2, {'photo.Publication_albums': 1, 'photo.Publication': 1})
# p3 출판물이 삭제되었다.
>>> Publicatioin.objects.all()
<QuerySet [<Publication: Publication object (1)>, <Publication: Publication object (2)>]>
# a3 앨범은 삭제되지 않았다.
>>> Album.objects.all()
<QuerySet [<Album: Django>, <Album: Nature>, <Album: TestAlbum1>, <Album: 국가별>, <Album: 사람들>]>
# 연결이 끊어져 p3 조회가 안된다.
>>> p3.albums_all()
Trackback (most recent call last):
File "<console>", line 1, in <module>
File "/home/......related.py", line 527, in __get___
....
ValueError: "<Publication: Publication object (None)>" needs to have a value for field "id" before this many-to-many relationship can be used.
# a3 앨범에 연결된 출판물이 없습니다.
>>> a3.publication_set.all()
<QuerySet []>
|
cs |
참고 : [실전편] Django 를 활용한 쉽고 빠른 웹개발 파이썬 웹프로그래밍 (김석훈) , 설명예제