7. 분류·댓글처럼 아무 모델이나 가리킬 수 있는 범용 모델을 정의할 수 있나요?¶
다음 모델을 봐 주세요.
class Category(models.Model):
name = models.CharField(max_length=100)
# ...
class Meta:
verbose_name_plural = "Categories"
class Hero(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
# ...
class Villain(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
# ...
여기서 Category
모델은 범용 모델로 고쳐 정의할 수 있습니다. 다른 모델에도 분류를 적용하고 싶을 테니까요. 다음과 같이 수정하면 됩니다.
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
# ...
class FlexCategory(models.Model):
name = models.SlugField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Hero(models.Model):
name = models.CharField(max_length=100)
flex_category = GenericRelation(FlexCategory, related_query_name='flex_category')
# ...
class Villain(models.Model):
name = models.CharField(max_length=100)
flex_category = GenericRelation(FlexCategory, related_query_name='flex_category')
# ...
수정한 코드에서는 FlexCategory
모델에 외래 키 필드(ForeignKey
) 하나와 양의 정수 필드(PositiveIntegerField
) 하나를 정의하여 범용 외래 키 필드(GenericForeignKey
)를 사용할 수 있도록 하였습니다. 그리고 분류를 이용할 모델에 범용 관계 필드(GenericRelation
)를 추가했습니다.
FlexCategory
모델의 데이터베이스 스키마는 다음과 같이 정의됩니다.
Hero
모델의 항목을 분류할 때는 다음과 같이 합니다.
hero = Hero.objects.create(name='Hades')
FlexCategory.objects.create(content_object=hero, name="mythic")
‘ghost’로 분류된 Hero
를 구하려면 다음과 같이 조회합니다.
Hero.objects.filter(flex_category__name='ghost')
위의 ORM 코드가 생성하는 SQL 질의문은 아래와 같습니다.
SELECT "entities_hero"."name"
FROM "entities_hero"
INNER JOIN "entities_flexcategory" ON ("entities_hero"."id" = "entities_flexcategory"."object_id"
AND ("entities_flexcategory"."content_type_id" = 8))
WHERE "entities_flexcategory"."name" = ghost