TIL DAY 11 || Django ManyToMany Field

TK·2021년 2월 28일
1

TIL

목록 보기
16/55

Django 의 models.ManyToManyField 에 대해 알기 전에는 그냥 A와 B 두 테이블을 동시에 참조하는 중간 테이블 C를 직접 만들어서 각각 C -> A, C -> B 구조를 만들었다.

하지만 이렇게 했을 때는, Django 에서 ManyToMany 로 제공해주는 메소드를 사용하지 못하기 때문에 효율성이 떨어진다.

그렇기 때문에 이번엔 ManyToMany 를 사용하여 모델링을 해보았다.

다음은 저번 스타벅스 페이지 모델링 과제에서 리뷰 받은 것을 토대로 Django 에서 모델링을 해보았다.

그 중 이번에 볼 것은 Drink, Allergy, AllergyDrink 테이블이다.

Starbucks Modeling Example

먼저 Drink 와 Allergy 테이블은 N:N 즉, ManyToMany relation 이다. 왜냐하면 한 음료는 여러가지의 알러지를 포함할 수 있고, 하나의 알러지도 여러가지 음료에 속할 수 있기 때문이다.

그래서 이전에 했던 방식은 AllergyDrink 테이블을 만들어서 Drink 와 Allergy 테이블을 참조하도록 했었다.

하지만 models.ManyToManyField 를 알고나서 바로 이 방법을 적용시켜보기로 했다.

  • Drink 클래스에서 allergy_drink 변수에 models.ManyToManyField instance 를 할당했다.

  • 왜 두개의 테이블 중 굳이 Drink 클래스에 ManyToManyField 를 넣어 두었냐면, 그럴만한 이유가 있다.

  • 우리가 Drink 를 생각할 때 음료가 알러지를 포함한 성분이 있을 수 있다고 생각을 하지, 반대로 알러지가 어떤 음료 속에 포함된다고 생각하지 않기 때문이다. 전자가 훨씬 자연스럽기 때문에 그렇게 따른다. 이에 대한 설명은 Django 문서에도 나와있다.

  • ManyToManyField 의 첫 번째 파라미터에는 N:N 으로 관계를 지을 테이블명을 '테이블명' 또는 object 로 직접 받는다.

  • through 키워드 파라미터에 이미 만들어 놓은 중간 테이블(중개 모델) 클래스명을 입력하면 해당 클래스를 중간 테이블로 사용하겠다는 의미이다. 중간 테이블이 그저 두 테이블을 각각 참조하는 용도라면 이 파라미터에 값을 주지 않아도 자동으로 중간 테이블이 설정된다. 하지만 좀 더 detail 한 정보를 넣기 위해 추가적으로 컬럼을 넣은 중개 모델을 사용하는 경우에는 through 옵션에 자신이 만든 중간 테이블 클래스명을 넘겨주어야 한다.

  • 지금 예제의 경우에는 내가 AllergyDrink 클래스를 중간 테이블로 쓸 것이기 때문에 through='AllergyDrink' 라고 했다.

  • 중개모델을 직접 생성할 때, through_fields 옵션을 사용해서 관계를 형성하는 두 모델을 명시해줄 수 있다. 기능을 설명하기 위해 추가했지만 사실 이 예제의 경우 필요가 없다.

  • blank= 옵션은 필드 값이 넘어올 때 empty 값이 넘어와도 되는지의 여부를 결정한다. blank=True 는 필드에 empty 값이 넘어오더라도 오류 없이 데이터를 받는다.

  • 이 때 헷갈리지 말아야 할 개념으로 null=True 가 있다. 이는 db 의 컬럼 값 즉 null=True 필드 값을 NULL 로 바꿔주는 것이다.

Null : DB와 관련되어 있다. (database-related) 주어진 데이터베이스 컬럼이 null 값을 가질 것인지 아닌지를 정의한다.
Blank : 유효성과 관련되어 있다. (validation-related) form.is_valid()가 호출될 때 폼 유효성 검사에 사용된다.

그러므로 즉, null=True, blank=False 옵션을 가진 필드를 정의하는 것에는 문제가 없다. 이는 DB레벨에서는 해당 필드가 NULL 될 수 있지만, application 레벨에서는 required 필드인 것을 의미한다.

출처 :https://wayhome25.github.io/django/2017/09/23/django-blank-null/

How to Retreive Data from ManyToMany

ManyToMany relation 으로 연결된 테이블은 서로가 서로를 참조할 수 있다.

  • nitro_coffee 는 Drink 테이블 안에서 korean_name 컬럼 값이 '나이트로 바닐라 크림'인 row 의 QuerySet object 이다. 이름이 잘 생각이 안나서 <column_name>__startswith 로 기억이 나는 단어만으로 가지고 왔다.

  • milk_allergy 는 Allergy 테이블에서 name 값이 '우유' 인 row 의 QuerySet object 이다.

  • 이제 이 두개의 objects 를 AllergyDrink 중개모델을 통해 연결한다.

결과는 다음과 같다.

또한 중개모델 instance 에 접근하여 add(), create(), set() 메소드를 사용할 수 있다. 다음을 보자.

  • nitro_coffee 에서 중개모델로 접근한 뒤 다음과 같은 메소드로 데이터를 추가, 수정, 제거할 수 있다.
  1. add()
  2. create()
  3. set()
  4. remove()
  5. claer()

다음과 같이 직접 AllergyDrink 모델에 쿼리를 날려서 특정 row 에 해당하는 정보를 가져올 수도 있다.

같은 정보를 역방향으로 가져오고 싶을 수도 있을 것이다. 이 때는 Allergy object (milk_allergy) 로 부터 many-to-many reverse relationship 의 쿼리를 통해 다음과 같이 정보를 가져올 수 있다.

  • milk_allergy 에서는 allergydrink_set
    (<중개모델 클래스명 소문자>_set) 을 통해 중개모델에 역방향으로 접근할 수 있다.

즉, Allergy 모델에는 allergy_drink 컬럼이 없지만, Allergy 모델의 object 에서 allergydrink_set 으로 AllergyDrink object 에 접근가능하다.

Access by filter()

filter() 메소드 사용이 사실 가장 헷갈리는 부분 중 하나였는데, 그 이유는 ManyToMany instance 가 있는 모델의 object 와, 그게 없는 instance 에서 서로에게 접근하는 방식이 좀 다르기 때문이다.

  • ManyToMany instance 가 있는 Drink 모델에서 name 이 '우유'로 시작하는 모든 allergy_drink 를 찾는 쿼리는 다음과 같다.
    -->
    Drink.objects.filter(allergy_drink__name__startswith=='우유')
    -->
    <참조의 주체가 되는 모델의 ManyToManyField object 변수명__<참조를 당하는 모델 컬럼명>__startswith='시작단어'>

다대다 필드 인스턴스가 포함된 모델에서 다른 모델로 접근하려면 무조건 한번 중개모델을 거쳐야된다.

  • ManyToMany instance 가 없는 Allergy 모델에서 korean_name 이 '나이트로' 로 시작하는 모든 allergy 성분을 찾는 쿼리는 다음과 같다.
    -->
    Allergy.objects.filter(drink__korean_name__startswith=='나이트로')
    -->
    <참조당하는 모델명__참조당하는 모델의 컬럼명__startswith='시작단어'>

다대다 필드 인스턴스가 포함되지 않은 모델에서 다른 모델로 접근하려면 중개모델 없이 바로 모델명으로 접근가능하다.

Difference between drink_set and allergydrink_set

Allergy 모델 object milk_allergy 의

  • drink_set.all() 반환결과는 QuerySet 의 Drink object
    --> milk_allergy 가 포함된 모든 음료들
  • allergydrink_set.all() 반환결과는 QuerySet 의 AllergyDrink object
    --> milk_allergy 가 속한 AllergyDrink 중개모델의 row 값들

wrap-up

  • 아무리 많은 블로그를 참고해봤지만, Django 공식문서만큼 가장 자세하고 정확히 나온 블로그는 보지 못했다. 즉 공식문서에 다 나와있다는 말이 사실이라는 뜻이다. 영어라서 한국말보다는 아무래도 조금 느리긴 하지만 대학생활동안 해외교환학생이랑 영어수업같은 것들이 도움이 정말 많이 되긴 한 것 같다.

  • 이론도 중요하지만 아무리 외운다고해서 잘 외워지는 것이 아니라는 것을 깨달았다. 이론은 베이스로 깔고 가고 코딩은 체화되어야 하는 것 같다.

  • ManyToManyField 에서 헷갈리는 것은 참조의 주체와 참조의 대상 간 서로에게 접근하는 방법이 다르다는 것이기 때문에 이 점을 조심하자!

profile
Backend Developer

0개의 댓글