장고 ManyToManyField 정리

개발자 강세영·2022년 5월 10일
0

TIL

목록 보기
23/65

장고 model의 ManyToManyField는 유용하긴 하지만 헷갈리는 것들이 많아 정리가 필요하다
먼저 왜 foreignkey대신 manytomanyfield를 써야하는지 생각해봐야 한다.
영화배우와 영화의 관계나 피자와 토핑의 관계같은걸 연상하면 쉽다. 둘 다 다대다 관계의 좋은 예시이다. 이런 유형의 테이블들은 manytomanyfield를 사용하는게 여러 장점이 있다.

단 장고 ORM에는 manytomanyfield 기능이 있지만 다른 프레임워크에는 없을수도 있기 때문에 manytomanyfield 쓰지 않고 foreignkey로만 연결하는것도 연습해봐야 한다.

from django.db import models 


class Pizza(models.Model):

    name = models.CharField(max_length=30)
    toppings = models.ManyToManyField('Topping')

    def __str__(self):
        return self.name


class Topping(models.Model):

    name = models.CharField(max_length=30)

    def __str__(self):
        return self.name

데이터 입력 순서

다대다 관계로 묶인 테이블은 서로 간의 데이터 입력 순서가 중요하다.
배우와 영화 테이블이 있고 장고 models에서 영화테이블에서 배우테이블을 manytomanyfield로 설정한 경우, 배우 테이블부터 데이터를 입력해야 한다. 또한 두 테이블간의 관계를 만들때 반드시 두 테이블에 해당하는 데이터가 미리 입력돼있어야 한다. 예를 들어 파인애플 피자의 토핑은 파인애플과 베이컨이라고 할때, 파인애플 피자와 파인애플 토핑은 입력돼있지만 베이컨 토핑은 없는 상황을 생각해보자. 이 상태에서 파인애플 피자의 토핑 목록에 베이컨 토핑을 추가한다고 입력해보면 에러가 난다.

related_name 옵션을 사용하지 않았다면 관계되는 모델의 소문자 클래스명_set을 사용해서 관계되는 값을 찾을수 있다.

canadian_bacon.pizza_set.all()
<QuerySet [<Pizza: Hawaiian>]>
canadian_bacon.pizzas.all()
<QuerySet [<Pizza: Hawaiian>]>

related_name 옵션을 사용한 경우 _set 대신 좀더 알기쉬운 이름을 사용할수있다.
related_name 옵션값은 소문자로 해당 모델 클래스명의 복수형으로 쓰는게 좋으며 이름짓기가 매우 중요하다. related_name을 엉뚱한 값으로 해놓으면 코드 유지보수나 로직에 문제가 많아진다.
참조해준 객체 입장에서 related_name을 설정해주는 것이 좋다.

class Pizza(models.Model):
    ...
    toppings = models.ManyToManyField('Topping', related_name='pizzas')

위 예시에서 related_name='toppings'가 아닌 이유? toppings로 하면

canadian_bacon.toppings.all()

이런식으로 입력해야 되는데 직관적이지 못하다. 나는 베이컨 토핑이 들어가는 피자를 찾고싶은 상황이다.

canadian_bacon.pizzas.all()

이렇게 찾는게 직관적이다.

양쪽의 관계 데이터 추가하기

>> cheese_pizza = Pizza.objects.create(name='Cheese')
>> mozzarella = Topping.objects.create(name='mozzarella')
>> mozzarella.pizzas.add(cheese_pizza)
>> mozzarella.pizzas.all()
<QuerySet [<Pizza: Cheese>]>

위 코드에서 add()로 모짜렐라 토핑과 치즈피자의 관계가 생성됐다.
변수를 여러개 만들어 놓고 add()로 한꺼번에 추가하면 되기 때문에 FK 필드보다 편리하다.

양쪽에서 데이터 접근하는 방법

>> Pizza.objects.filter(toppings__name__startswith='p')
<QuerySet [<Pizza: Pepperoni Pizza>, <Pizza: Hawaiian Pizza>]>

>> Topping.objects.filter(pizzas__name__contains='Hawaiian')
<QuerySet [<Topping: pineapple>, <Topping: Canadian bacon>]>

ManyToManyField를 사용하면서 얻는 이점

  1. 본질적으로 다대다 관계의 테이블은 mtom을 쓰는게 좋다
  2. FK만 이용할 경우 중간테이블을 직접 만들어줘야 하지만 mtom은 자동으로 중간 테이블을 만들어 준다.
  3. through 옵션으로 중간 테이블을 직접 설정해줄 수 있다.
  4. related_name 옵션으로 직관적인 관계설정이 가능하고 참조하는 코드가 깔끔하다. 반면에 FK필드만으로 다대다 관계를 만들면 중간테이블을 거쳐야 참조가 가능하다

참고자료
https://www.revsys.com/tidbits/tips-using-djangos-manytomanyfield/

0개의 댓글