당신은 멋쟁이, 우리는 장고쟁이~

0%

Django Models 8편 - Relationships (Many-to-many) A

Many-to-many Relationships


many-to-many 관계를 정의하기 위해서는, ManyToManyField 를 사용해 주면 됩니다.


다른 Field 타입을 사용하는것과 비슷하게 사용합니다. 클래스 속성에 포함해주는 방식으로 사용하면 됩니다.


ManyToManyField 는 위치 인자를 필요로 합니다.


관계된 모델을

ManyToManyField 의 위치인자로 넣어주어야 합니다


예를들면, 하나의 피자가 다수의 토핑 객체를 가진다면,


- 토핑은 다수의 피자에 있을수 있고, 각 피자는 다수의 토핑들을 가질수 있습니다.

이러한 관계는 아래의 형태로 표현될수 있습니다.


1
2
3
4
5
6
7
8
9
10
from django.db import models 

class Topping(models.Model):
# ...
pass


class Pizza(models.Model):
# ...
# toppings = models.ManyToManyField(Topping)


ForeignKey 와 마찬가지로, 재귀적인 관계를 생성할수도 있습니다. (객체가 many-to-many 관계를 자기 자신에게 갖는것). 그리고, 아직 정의되지 않은 모델과도 관계를 생성할수도 있습니다.


필수는 아니지만, 권장 사항으로는,

ManyToManyField 필드명은, 관계된 모델 객체의 복수형으로 표현합니다.


1
2
3
4
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
# toppings 라고 복수형의 필드로 사용

위 예시에서 Pizza 와 Topping, 두 모델을 예시로 들어봤는데.

두 모델중 한쪽에만 ManyToManyField 가 사용되어야 합니다. 양쪽 다 사용하지는 않습니다!


보통 ManyToManyField 인스턴스들은, Form 에서 수정될 객체에 생성됩니다.


위 예시에서는, Pizza 안에 toppings 가 들어가 있는데. Pizza 가 여러개의 topping 들을 가지고, 피자에 들어간 토핑을 고치는 개념이 Topping 이 Pizza 를 가지고, 토핑에 들어간 피자를 고치는것보다 자연스럽기 때문에.


Pizza 클래스안에 ManyToManyField 를 넣어준것입니다.


Extra fields on many-to-many relationships


many-to-many 관계에는, 추가적인 필드들이 존재합니다.


피자와 토핑같은 피자와 토핑을 섞고 매칭하는 다대다 관계를 다룰때는, 일반적인 ManyToManyField 만으로도 충분히 다룰수 있습니다.


하지만, 때때로, 우리는 두개의 모델 사이의 관계를 관련지어서 데이터들을 생각할 필요가 생깁니다.


예를들어서, 뮤지컬 그룹과 뮤지컬 그룹들에 속해 있는 뮤지션들의 관계를 생각해 봅시다.



한 사람과, 사람이 멤버로 속해 있는 그룹 사이에는 many-to-many 관계를 사용할수 있습니다. 따라서, ManyToManyField 를 사용하여 해당 관계를 표현할수 있습니다. 그렇지만, 멤버쉽에 대한 조금 더 많은 세부 사항들을 수집하고 싶어질지 모릅니다. 예를들면, 아래와 같은 사항들을 따로 기록해 놓고 싶을지 모릅니다.


  • 사람이 어떤 날짜에 그룹에 합류 하였는지
  • 언제 떠나갔는지
  • 가입 장소는 어디인지
  • 기타 정보

이러한 상황들에서, Django 는 many-to-many 관계를 관리하기 위한 모델을 명시할수 있게 해줍니다.


extra field 를 중간 모델에 넣어둘수 있습니다. 여기서 이야기 하는 중간 모델은, 새로운 모델로써, ManyToManyField 와 연관되어 있고, through 인자를 사용하여, 해당 모델이 중간 모델 역할을 할거라고 명시할수 있습니다.


조금 더 복잡한 many-to-many 관계를 관리하기 위해, 두 모델 사이에 어떠한 모델을 생성해 주고.

extra field 들을 중간 모델에 넣어주고 사용할수 있습니다.


중간 모델은 ManyToManyField 와 연관되어 있고, through 인자를 사용하여,

해당 모델이 중간 모델 역할을 할것이라고 명시할수 있습니다.


코드를 보면서 이해하는것이 더 빠를것입니다. 뮤지션 예시를 보면 아래와 같습니다.


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
from django.db import models 


class Person(models.Model):
name = models.CharField(max_length=128)

def __str__(self):
return self.name

class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
# through = "Membership" 을 넣어줌으로,
# Membership 이 Person 과 Group 사이의 중간 모델 역할을 하게 만들수 있음

def __str__(self):
return self.name


class Membership(models.Model):
# 중간 모델에서는, Person 과 Group 이 ForeginKey 로 등록 되어 있어야 함
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)



중간모델의 제약사항



  • 중간 모델은 반드시 하나의 소스모델을 가르키는 foreign Key 를 가져야 합니다 (예시에서는 Group 이 됩니다)
  • Group 모델이 바로 소스 모델인데, Group class 안에 through='Membership' 을 사용해서, Membership 이 Group 모델에 대한 중간 모델이 되게 만들어 주었습니다.
  • 따라서, 중간 모델인 Membership 에서 1개의 Foreign Key 를 소스 모델인 Group 에 지정해 주었습니다.

유의사항


소스모델이 두개 이상이라면, ManyToManyField.through_fields 를 사용해서, Django 가 데이터베이스 관계를 위해 사용할 foreign key 들을 명시적으로 지정해 줘야 합니다.

만약 foreign key 가 한개 이상 있고, through_fields 가 지정되어 있지 않으면, 유효성 에러가 발생하게 됩니다.


비슷한 제약 사항이 타겟 모델에 쓰이는 Foreign key 에도 적용됩니다 (예시에서는, Person 모델이 타겟 모델입니다)

- 중간 모델을 통해서 many-to-many 관계를 자기자신에 갖는 모델들은, 두개의 Foreign key 들을 같은 모델에 지정하는것이 허용됩니다. 하지만, 이것은 many-to-many 관계의 양쪽 사이드로 취급됩니다. 만일 2개 이상의 foreign key 들이 존재한다면, through_fields 를 사용해서 지정을 해줘야 합니다. 그렇지 않으면, 유효성 에러가 발생 합니다.