[Django] Model - Many To Many (Intermediary Model)
Many To Many
Many To Many 의 기본 사용법 참조 : [Django] Model - Many To Many
Intermediary Model
Many To Many 관계를 생성하면 M : N
의 관계를 1 : M : 1
로 변형될 수 있도록
Intermediate Table 이 자동생성된다.
하지만 자동생성되는 Intermediate Table 의 경우에는 ForeginKey 를 통해 두개의 테이블을
연결시켜줄 뿐 다른 데이터는 없다.
데이터를 추가하고 싶은 경우 (연결관계가 생성된 시간, 추가설명 등) 자동생성 테이블이 아닌 직접 모델을 만들어야한다.
그 때 직접 만든 모델을 Intermediary Model 이라고 하며 직접 생성 후,
ManyToManyField
의 속성 through
를 통해 지정할 수 있다.
Django Model Code
[Django] Model - Many To Many 에서 사용한 모델 코드에 Intermediary Model 을 생성해보겠다.
아래의 Intermediary Model 에서는 Customer
와 Fruit
의 관계에서
구매시간(time), 구매수량(quantity)을 추가하였다.
from django.db import models
class Customer(models.Model):
name = models.CharField(max_length=20)
choice = models.ManyToManyField(
'Fruit',
through='Purchase', # Specify Intermediary Model
related_name='buyers',
)
def __str__(self):
return self.name
# -- Intermediaty Model -- #
class Purchase(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
fruit = models.ForeignKey('Fruit', on_delete=models.CASCADE)
quantity = models.IntegerField()
time = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{} : {} ({})'.format(
self.customer.name,
self.fruit.name,
self.quantity
)
# ------------------------ #
class Fruit(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
DateTimeField
의auto_now_add=True
속성을 통해 해당 데이터가 생성되는 시간을 자동으로 필드값을 채워준다.
auto_now
는 데이터가 생성된 시간 + 수정된 시간을 자동으로 필드값을 채운다.
Practice
위의 구현된 모델을 통해 실제 인스턴스(객체)를 생성하고 확인해보자.
실습을 위해 market 이라는 이름을 가진 app을 생성하고 해당 앱의 models.py 에 구현코드를 작성한다.
이후makemigrations
와migrate
를 하였음을 가정한다.
from market.models import Customer, Fruit, Purchase
a = Customer.objects.create(name='a') # 손님 a 생성
b = Customer.objects.create(name='b') # 손님 b 생성
apple = Fruit.objects.create(name='apple') # 과일 apple 생성
grape = Fruit.objects.create(name='grape') # 과일 grape 생성
orange = Fruit.objects.create(name='orange') # 과일 orange 생성
# 손님 : a / 과일 : apple / 수량: 1
Purchase.objects.create(customer=a, fruit=apple, quantity=1)
# 손님 : a / 과일 : grape / 수량: 2
Purchase.objects.create(customer=a, fruit=grape, quantity=2)
# 손님 : a / 과일 : orange / 수량: 3
Purchase.objects.create(customer=a, fruit=orange, quantity=3)
# 손님 : b / 과일 : grape / 수량: 7
Purchase.objects.create(customer=b, fruit=grape, quantity=7)
# 손님 : b / 과일 : orange / 수량: 3
Purchase.objects.create(customer=b, fruit=orange, quantity=3)
Purchase.objects.all() # Purchase List
>>> <QuerySet [<Purchase: a : apple (1)>, <Purchase: a : grape (2)>, <Purchase: a : orange (3)>, <Purchase: b : grape (7)>, <Purchase: b : orange (3)>]>
Notice
Intermediate Model 를 정의하고 through
로 지정한 ManyToManyField
는
아래의 메서드를 사용할 수 없다.
add()
create()
set()
remove()
a.choice.add(apple)
>>> AttributeError
원인 : Intermediary Model 을 정의함에 따라 단순히 Customer
와 Fruit
의 관계만(ForeginKey)을 추가, 제거하는 것이 아니다.
이 두 모델의 관계를 만들기 위해서는 (관계 데이터) + quantity
, time
이 필요하다.
따라서 두 모델의 관계를 추가하기 위해서는 Intermediary Model 에 해당하는 Purchase
모델을 호출하여 생성하여야 한다.
Purchase.objects.create(customer=<customer>, fruit=<fruit>, quantity=<int>)
하지만 clear()
는 사용가능하다.
clear()
의 경우는 어떠한 값이 필요한지는 신경쓰지 않는다.
그냥 모두 지우는 것이기 때문이다.
a.choice.clear()
a.choice.all()
>>> <QuerySet []>
역방향 참조는 문제없이 작동한다.
grape.buyers.all()
>>> <QuerySet [<Customer: a>, <Customer: b>]>
DataBase
Table Name은
AppName_ClassName
소문자화로 만들어진다.
Table List
-
market_customer
-
market_fruit
-
market_purchase (직접 생성한 Intermediary Model 에 의해 생성된 Table)
market_customer
id | name |
---|---|
1 | a |
2 | b |
market_fruit
id | name |
---|---|
1 | apple |
2 | grape |
3 | orange |
market_purchase
ManyToManyField
의 through
로 지정된 테이블
id | quantity | time | customer_id | fruit_id |
---|---|---|---|---|
1 | 1 | 2018-12-17 –:–:–.—— | 1 | 1 |
2 | 2 | 2018-12-17 –:–:–.—— | 1 | 2 |
3 | 3 | 2018-12-17 –:–:–.—— | 1 | 3 |
4 | 7 | 2018-12-17 –:–:–.—— | 2 | 2 |
5 | 3 | 2018-12-17 –:–:–.—— | 2 | 3 |
-
quantity
: 구매수량 -
time
: 구매시간 (auto_now_add=True
속성에 의해 생성시 생성시간이 자동으로 채워진다.) -
customer_id
: market_customer 으로 연결된 ForeignKey (자동생성되는 테이블에 기본적으로 있는 필드) -
fruit_id
: market_fruit 으로 연결된 ForeignKey (자동생성되는 테이블에 기본적으로 있는 필드)
Leave a comment