일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- AWS
- TypeScript
- merge
- Props
- RDS
- git
- NeXT
- socket io
- lambda
- crud
- SSA
- docker
- dict
- node
- wetube
- S3
- pandas
- react
- 중급파이썬
- Class
- 파이썬
- SAA
- Vue
- 채팅
- EC2
- async
- MongoDB
- flask
- 카톡
- 튜플
- Today
- Total
초보 개발자
many-to-many 본문
ManyToMany
데이터베이스의 Many-to-Many relationship(이하 다대다 관계)는 처음 접하는 사람들을 힘들게 한다. 한 테이블의 여러 레코드가 다른 테이블의 여러 레코드와 연결되어 있는 관계. 말만 들어서는 감이 오지 않으니 예를 들자면, 피자와 토핑 사이의 관계라고 할 수 있을 것이다. '피자'라는 테이블의 피자(페퍼로니피자, 치즈피자 등)는 '토핑' 테이블의 토핑(치즈, 페퍼로니, 올리브, 양파 등)을 여러 개 가질 수 있고, 그 반대도 마찬가지이다. 장고에서는 이러한 데이터들의 관계를 ManyToManyField에서 정의할 수 있다.
class Pizza(models.Model):
name = models.CharField(max_length=20)
toppings = models.ManyToManyField('Topping')
class Topping(models.Model):
name = models.CharField(max_length=20)
위의 예시처럼 피자 테이블과 토핑 테이블을 만들었다면, 둘 중 하나에서 상대방을 참조하는 필드를 만들고 ManyToManyField를 정의하면 된다. 여기서는 피자 테이블에서 토핑 테이블을 참조하는 toppings 필드를 생성했다.
장고 공식문서에서는 ManyToManyField를 정의하면, 자동으로 두 테이블 사이의 관계를 관리해주는 중간 테이블을 생성한다고 되어 있다. 이 테이블은 우리가 작성한 모델에는 없지만, 데이터베이스를 확인하면 다대다관계의 두 테이블 이름을 _로 이어준 별도의 테이블이 생성된 것을 볼 수 있다.
이 중간 테이블은 두 테이블의 id를 각각 필드로 가지고 있다. 그렇기 때문에 각 테이블에 데이터가 존재하지 않으면 중간 테이블에도 primary key가 존재하지 않는다.
위에서 작성한 예시를 보면, 피자 테이블에 토핑 테이블을 참조하는 필드가 존재한다. 그래서 한 피자에 있는 토핑들을 가져오는 것은 크게 문제가 없다.
#피자이름
Pizza.objects.all()
#피자에 올린 토핑 이름
Pizza.objects.get(name='파파존스').toppings.all()
하지만 토핑이 들어간 피자들을 가져오기 위해
cheeze.pizzas.all()을 하면, AttributeError: 'Topping' object has no attribute 'pizzas' 와 같이 '그런 속성 없다' 는 에러가 뜬다. 사실 조금 생각해보면 토핑 테이블에는 피자와 관련된 데이터는 아무것도 없다. 그렇기 때문에 직접적으로 가져올 수 없는 것이다.
그래서 참조되는 테이블(토핑)에서 참조하는 테이블(피자)의 데이터를 가져오기 위해, 장고에서는 참조하는 테이블의 이름 뒤에 _set를 붙여주어야 한다고 명시했다.
즉 모델명(소문자)_set 이렇게 해줘야함 Topping.objects.get(topping_name='치즈').pizza_set.all()
>> pineapple.pizza_set.all()
<QuerySet [<Pizza: Hawaiian>]>
그런데 _set을 붙여주기 싫다면? 해결책은 있다. 바로 필드 속성에 related_name을 아래와 같이 정의해 주는 것이다.
toppings = models.ManyToManyField('Topping', related_name='pizzas')
처음에 related_name을 보면 뭔가 의아할 것이다. 필드명은 토핑인데 왜 이름을 'pizzas'라고 붙였을까? 그 이유는 related_name이 '참조되는 테이블이 참조하는 테이블의 데이터를 가져오고 싶을 때 사용하는 이름'을 정의하는 것이기 때문이다. 이 경우 토핑이 피자의 데이터를 가져오고 싶을 때 pizza_set 대신 pizzas를 쓸 수 있다는 것이다.
pineapple.pizzas.all()
<QuerySet [<Pizza: Hawaiian>]>
related_name을 정의해 주니 _set 없이도 토핑에서 피자의 데이터에 접근한 것을 볼 수 있다.
여기서 만약 related_name을 추가한다면 전에 사용하였던 모델(소문자)_set명령어는 더이상 유효하지 않게된다. 둘 중 하나만 선택하여 써야 한다.
데이터 간의 관계성을 가지게 하기 위해 hawaiian_pizza.toppings.add(pineapple)피자 테이블에서 토핑을 추가할 수도 있지만,
hawaiian_pizza.toppings.add(pineapple)
반대로 아래와 같이 토핑 테이블에서 토핑이 들어간 피자를 추가할 수도 있다.
즉 피자에서도 토핑을 추가할 수있고, 토핑에서도 피자를 추가할 수 있다는 말이다.
mozzarella = Topping.objects.create(name='mozzarella')
>> mozzarella.pizzas.add(cheese_pizza)
여기서 cheeze_pizza는 아무 문자나 적은 것이 아니라 Pizza모델의 객체이다.
이 결과로 모짜렐라는 치즈피자의 토핑으로 올라가고, 피자치즈의 토핑에도 모짜렐라가 추가되었다.
따라서 피자치즈의 토핑을 검색하면 치즈도 나오고 치즈가 들어있는 피자들을 검색하면 피자치즈도 추가된 걸 볼 수 있을 것이다.
'AI 웹개발 트랙 - 내배캠 > 6주차' 카테고리의 다른 글
6주차 WIL (0) | 2022.01.22 |
---|---|
django views class (0) | 2022.01.21 |
AWS EC2 배포 및 가비아 연결 (0) | 2022.01.21 |
django {% url %} (0) | 2022.01.21 |
django sparta 강의 정리 및 의문점 (0) | 2022.01.19 |