예를 들어 슈퍼마켓과 아이스크림이 있다고 가정해보자.
한 슈퍼마켓은 여러 아이스크림을 납품받을 수 있고, 한 아이스크림은 여러 슈퍼마켓에 납품될 수 있다.
이것이 다대다 관계이며, django에서는 ManyToManyField로 정의된다.
공식문서 예시를 보자.
class Publication(models.Model):
title = models.CharField(max_length=30)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
Publication과 Article의 각 객체를 1개씩 생성한다.
p1 = Publication(title='The Python Journal')
p1.save()
a1 = Article(headline='Django lets you build web apps easily')
a1.save()
각 객체를 다대다 관계로 엮으려면 add()를 사용하면 된다.
즉, add()는 객체를 추가하는 개념이 아니라 관계를 associate 하는 것이다.
a1.publications.add(p1)
여러 개 객체를 add하는 것도 가능하다.
#publication 객체를 추가로 생성했다고 가정한다
a1.publications.add(p1,p2...)
create()를 사용하면 객체 생성, 관계 연결을 모두 진행할 수 있다.
new_p = a1.publications(title='Highlights for Children')
title = 'Highlights for Children'이다 remove()와 clear() 역시 add()와 마찬가지로 관계를 다루며, 관계를 삭제(disassociate)해준다. 해당 모델이나 인스턴스 자체를 삭제하는게 아니라는 점.
그리고 remove()와 clear()는 다대다 관계 설정 시 null=True 로 설정되어 있을 때 사용 가능하다.
#개별삭제
p1.article_set.remove(a1)
#일괄삭제
p1.article_set.clear()
#변경
p1.article_set.set([a2])
이 모든 관계 관련 기능/작업은 RelatedManager 클래스로 인해 가능한데, ReleatedManager는 모델(테이블) 간 관계, 관계와 관련된 쿼리 및 기능을 담당한다.
A “related manager” is a manager used in a one-to-many or many-to-many related context.
1:1 관계필드나 다대다 관계필드에서만 사용되며, 이 ReleatedManager 덕분에 위 예시에서 사용됐던 메소드들이 a1.publications 나 p1.article_set에서 사용 가능한 것이다.
ManyToManyField는 ForeignKey와 동일한 역할을 하기 때문에 ForeignKey로 두 테이블을 연결하는 방법도 사용 가능하나, ManyToManyField의 이점은:
ForeignKey로 다대다 관계를 형성하면, 중간 테이블을 별도로 만들어줘야 한다 ForeignKey로 다대다 관계를 형성하면, 중간 테이블을 거치고 참조/역참조 등을 해야해서 불편하고 코드도 길어진다 class Publication(models.Model):
title = models.CharField(max_length=30)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
위 예시로 설명하면, ManyToManyField로 정의되는 순간 django가 자동으로 article_publication 이라는 중간 테이블을 만들어 준다.
<중간테이블 article_publication>
| ID | publication_id | article_id |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | ... | ... |
그래서 단순 ForeignKey 로 정의된 관계와 달리 더 간단하게 상대편으로 접근 가능하다.
#Article의 객체인 a1에서 바로 publication 데이터에 접근
a1.publications.get().title
#만약 ForeignKey로 다대다 관계를 설정했다면?
a1.article_publication.get().publications.title
마지막으로 ManyToManyField의 주요 arguments 들을 살펴보자.
article이라고 정의했다면limited_choices_to = {is_staff:True}through_fields = (A의 field,B의 field)를 명시해 혼란을 없앤다 공식문서 예제:
https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.ManyToManyField
https://docs.djangoproject.com/en/4.0/ref/models/relations/
https://docs.djangoproject.com/en/4.0/ref/models/relations/#django.db.models.fields.related.RelatedManager