다음과 같이 User, Project, Ticket 그리고 TimeLine 모델들이 있다.
class Ticket(models.Model):
name = models.CharField(max_length=200, verbose_name=_("name"))
slug = models.SlugField(max_length=250, null=False, blank=True, verbose_name=_("slug"))
class Project(models.Model):
name = models.CharField(max_length=200, verbose_name=_("name"))
slug = models.SlugField(max_length=250, null=False, blank=True, verbose_name=_("slug"))
class User(models.Model):
name = models.CharField(max_length=200, verbose_name=_("name"))
slug = models.SlugField(max_length=250, null=False, blank=True, verbose_name=_("slug"))
class Timeline(models.Model):
involved_object = *****
event_type = models.CharField(max_length=250, default="created")
만약 타임라인에 "유저 생성, 프로젝트 생성, 태스크 생성" 과 같은 유저의 활동을 저장하고 싶다고 한다면, 우리는 세 개의 모델(User, Project, Task)을 ForeignKey
필드로 지정해야한다. 이것은 좋은 프로그래밍 습관이 아니다. 이를 해결하기 위해, Django의 contenttypes 는 어떤 모델들과도 관계를 정의하게 해주는 GenericForeignKey 라는 특별한 필드 타입을 제공한다.
위의 시나리오를 해결할 수 있는 방법이 있다. GenericForeignKey를 세팅하는 것은 세 단계로 나뉜다.
content_type
이다.object_id
이다.content_type
고 object_id
이며, 기본값이기 때문에 생략이 간읗다.위의 세 단계를 따라하면, TimeLine 모델은 다음과 같이 변한다.
class Timeline(models.Model):
content_type = models.ForeignKey(ContentType, related_name="content_type_timelines")
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
event_type = models.CharField(max_length=250, default="created")
만약 프로젝트 생성 후, "프로젝트 생성" 이라는 이벤트를 저장하고 싶다면 아래의 코드가 우리를 도와줄 것 이다.
t1 = TimeLine(content_object=project_object)
t1.save()
하지만 GenericForeignKey 가 구현된 방식 때문에, filter()
와 exclude()
같은 필터에서 content_object
필드를 데이터베이스 API를 통해 직접적으로 사용할 수 없다. GenericForeignKey 가 일반적인 필드가 아니기 때문에, 다음과 같은 코드는 동작하지 않는다.
TimeLine.objects.filter(content_object=project_object)
# This will also fail
TimeLine.objects.get(content_object=project_object)
Project 객체로 TimeLine 객체를 얻으려면, 다음과 같은 단계를 거쳐야 한다.
from django.contrib.contenttypes.models import ContentType
contenttype_obj = ContentType.objects.get_for_model(project_object)
object_id
가 project_object.id
로 저장되어 있다.TimeLine.objects.filter(object_id=project_object.id, content_type=contenttype_obj)
이 포스팅의 원문은 MicroPyramid 블로그에 있습니다.