[django] ManyToManyField에 대하여

EMMA·2022년 8월 5일
0

ManyToMany, 다대다 관계

예를 들어 슈퍼마켓과 아이스크림이 있다고 가정해보자.
한 슈퍼마켓은 여러 아이스크림을 납품받을 수 있고, 한 아이스크림은 여러 슈퍼마켓에 납품될 수 있다.

이것이 다대다 관계이며, 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')
  • a1과 associate된 new_p를 생성한다
  • new_p의 값은 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.publicationsp1.article_set에서 사용 가능한 것이다.


ManyToManyField의 benefits

ManyToManyFieldForeignKey와 동일한 역할을 하기 때문에 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>

IDpublication_idarticle_id
111
212
3......

그래서 단순 ForeignKey 로 정의된 관계와 달리 더 간단하게 상대편으로 접근 가능하다.

#Article의 객체인 a1에서 바로 publication 데이터에 접근
a1.publications.get().title 

#만약 ForeignKey로 다대다 관계를 설정했다면?
a1.article_publication.get().publications.title

ManyToManyField의 arguments

마지막으로 ManyToManyField의 주요 arguments 들을 살펴보자.

  • related_name
    • 역참조 시 사용할 수 있는 이름 설정 (FK도 동일함)
    • 따로 정의하지 않으면 클래스명을 사용한다
  • related_query_name
    • 역참조 queryset filter시 사용할 수 있는 이름 설정 (FK도 동일함)
    • 만약 Article 테이블에서 해당 이름을 article이라고 정의했다면
      • Publication.objects.filter(article__headline="python")
      • 따로 정의하지 않으면 클래스명을 사용한다
  • limit_choices_to
    • 특정 조건을 정의해 이에 해당하는 내용만 사용할 수 있게 해준다
    • dictionary 형태로, field와 값을 작성한다
    • e.g.) limited_choices_to = {is_staff:True}
  • through
    • 중간 테이블을 별도로 커스터마이징했다면, through를 통해 해당 테이블이 중간 테이블임을 명시해야 한다
  • through_fields
    • 구체적으로 어떤 field를 통해 다대다 관계를 맺느냐에 대한 명시다
    • 만약 A-B가 다대다관계고, C라는 중간테이블을 만들었다고 가정하자. 그리고 C에는 A를 외래키로 참조하는 field가 또 있다.
      • C에는 A에 대한 외래키를 2개 갖게되고, django는 다대다관계를 읽을 때 어떻게 읽어야 할 지 모르는 상태가 된다
      • 이를 위해 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

profile
예비 개발자의 기술 블로그 | explore, explore and explore

0개의 댓글