예를 들어 슈퍼마켓과 아이스크림이 있다고 가정해보자.
한 슈퍼마켓은 여러 아이스크림을 납품받을 수 있고, 한 아이스크림은 여러 슈퍼마켓에 납품될 수 있다.
이것이 다대다 관계이며, 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