ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Django] GenericForeignKey
    파이썬/Django 2021. 5. 23. 10:25
    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 Villian(models.Model):
        name = models.CharField(max_length=100)
        category = models.ForeignKey('Category', on_delete=models.CASCADE)

    이렇게 두개의 모델을 ForeignKey 필드로 지정하면 좋은 프로그래밍 습관이 아니다. 이를 해결하기 위해, Django의 contenttypes 라는 프레임워크를 지원하여 어떤 모델들과도 관계를 정의하게 해주는 GenericForeignKey 라는 특별한 필드 타입을 제공한다.

    GenerignForeignKey를 세팅하기 위해 세 가지 단계가 있다.

    1. ContentType 객체를 ForeignKey 로 정의한다. 이 필드의 이름은 대부분 content_type이다.
      • 이것은 django_content_type 테이블에서 지정된 특정 테이블을 지칭한다. 해당 모델 타입의 객체를 얻고 싶다면 django_content_type.id와 매칭되는 Category 테이블의 content_type.jd를 유의하면 된다.
    2. 연관된 모델의 기본키 값을 저장할 수 있는 필드를 지정한다. 대부분 PositiveIntegerField 타입이며 이름은 object_id 이다.
      • 반드시 Primary Key이어야 한다.
    3. GenericForeignKey 타입 필드를 만들고 위의 두 필드의 이름을 인자로 넘겨준다. 이 인자들의 값은 content_typeobject_id 이다.
    4. 범용 모델을 이용할 모델에 범용 관계 필드 GenericRelation을 추가한다.
      • 이것은 컨텐츠 모델이 ContentType 프레임워크를 통해 어떤 모델과 연관성이 있다는 것을 알기 위해 정의되어 있는 것이다

    따라서, 세 단계를 진행한 후 만든 Category의 모델은

    class Category(models.Model):
        name = models.CharField(max_length=100)
        content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
        object_id = models.PositiveIntegerField()
        content_object = models.GenericForeignKey('content_type', 'object_id')
        class Meta:
            verbose_name_plural = 'Categories'
    
    
    class Hero(models.Model):
        name = models.CharField(max_length=100)
        category = models.GenericRelation('Category', on_delete=models.CASCADE)
    
    
    class Villian(models.Model):
        name = models.CharField(max_length=100)
        category = models.GenericRelation('Category', on_delete=models.CASCADE)​

    주의!!

    GenericFoerignKey는 DB Table의 Column에 존재하지 않는다! 그렇기 때문에 다른 서비스에서 Category 모델의 content_object 필드를 찾으려고 하면 안된다!

    또한 Database API를 사용할 수 없다.

     

    Cateogory.objects.filter(content_object=ironman) # Fail
    Cateogory.objects.get(content_object=ironman) # Fail

    댓글

Designed by Tistory.