반응형
through와 through_fields와 through_default에 대한 정리
1. through
다대다 관계를 나타낼 때, custom 한 중개모델을 지정할 때 사용한다.
xxxxxxxxxx
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Meta:
db_table = "person"
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through="Membership",
) # Membership은 Person과 Group 관계의 중개모델 역할은 하는데, 이를 위와같이 지정해줄 수 있다.
def __str__(self):
return self.name
class Meta:
db_table = "group"
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
class Meta:
db_table = "membership"
2. through_fields
1번에 제시된 코드를 기준으로 봤을 때, Group 모델을 소스모델, Person 을 타겟모델로 본다.
소스모델 혹은 타겟모델과 관련해서 중개모델에서 같은 모델 내 외래키가 2개 이상이면, makemigrations
단계부터 에러가 발생하게 된다. - 자세한 것은 중개모델의 제한사항에서 2번을 참고하자.
이를 해결하기 위해서, through_fields
를 지정해준다.
xxxxxxxxxx
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Meta:
db_table = "person"
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through="Membership",
through_fields=("group", "person"), # (소스모델, 타겟모델) 순서로 명시해준다
)
def __str__(self):
return self.name
class Meta:
db_table = "group"
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
) # 멤버쉽에 초대한 사람과 관련된 inviter 필드가 추가되었다. 이로써, 같은 Person(타겟 모델)에 대해서 외래키가 2개가 발생한 상황
invite_reason = models.CharField(max_length=64)
class Meta:
db_table = "membership"
위와 같이 지정해주면, 오류없이 정상적으로 makemigration이 동작한다. - 더 자세한 내용은 중개 모델의 제한 사항에서 실제 오류가 발생하는 것을 확인 할 수 있다.
3. through_defaults
through_defaults
는 RelatedManager
내 메소드인 add
, set
, create
등의 인자로 사용이된다
그중에서 add
만 살펴보자면, 새로운 중개 모델 인스턴스를 지정할 때 사용한다. 중개모델 인스턴스가 생성되기 전에 딱 한번만 평가된다. through_defaults
예제를 보면 아래와 같다.
xxxxxxxxxx
In [1]: person = Person.objects.create(name='권태형')
INSERT INTO "person" ("name")
VALUES ('권태형')
Execution time: 0.001453s [Database: default]
In [2]: group = Group.objects.create(name='파이썬모임')
INSERT INTO "group" ("name")
VALUES ('파이썬모임')
Execution time: 0.001994s [Database: default]
In [3]: group.members.all()
Out[3]: SELECT "person"."id",
"person"."name"
FROM "person"
INNER JOIN "membership"
ON ("person"."id" = "membership"."person_id")
WHERE "membership"."group_id" = 1
LIMIT 21
Execution time: 0.000240s [Database: default]
<QuerySet []>
In [5]: group.members.add(person, through_defaults={'date_joined': '2020-01-01', 'invite_reason': '그냥'})
BEGIN
Execution time: 0.000125s [Database: default]
SELECT "membership"."person_id"
FROM "membership"
WHERE ("membership"."group_id" = 1 AND "membership"."person_id" IN (1))
Execution time: 0.000183s [Database: default]
INSERT INTO "membership" ("person_id", "group_id", "date_joined", "invite_reason") SELECT 1,
1,
'2020-01-01',
'그냥'
Execution time: 0.000397s [Database: default]
In [6]: group.members.all()
Out[6]: SELECT "person"."id",
"person"."name"
FROM "person"
INNER JOIN "membership"
ON ("person"."id" = "membership"."person_id")
WHERE "membership"."group_id" = 1
LIMIT 21
Execution time: 0.000137s [Database: default]
<QuerySet [<Person: 권태형>]>
반응형
'개발 > 장고' 카테고리의 다른 글
[DRF] Serializers - 개요 (0) | 2021.01.31 |
---|---|
[해설과 함께 읽는 Django 문서] Models - 다대다 관계에서의 추가 필드 (0) | 2021.01.30 |
[Django] 다대다 관계의 모델을 직접 활용해 보자 (0) | 2021.01.30 |
[Django] 다대다 관계에서 중개 모델의 제한 사항 (0) | 2021.01.30 |
[해설과 함께 읽는 Django 문서] Models - 다 대 다 관계 (0) | 2021.01.29 |