자식 클래스의 이름을 모르고 django에서 개체의 자식 클래스에 액세스하려면 어떻게해야합니까?


81

Django에서 부모 클래스와 그로부터 상속되는 여러 자식 클래스가있는 경우 일반적으로 parentclass.childclass1_set 또는 parentclass.childclass2_set을 통해 자식에 액세스하지만 원하는 특정 자식 클래스의 이름을 모르는 경우 어떻게해야합니까?

자식 클래스 이름을 몰라도 부모-> 자식 방향으로 관련 개체를 가져 오는 방법이 있습니까?


79
@ S.Lott 이런 종류의 응답은 정말 오래되었습니다. 유스 케이스를 생각할 수 없다고해서 질문자가 가지고 있지 않다는 의미는 아닙니다. 모든 종류의 다형성 동작에 대해 서브 클래 싱을 사용하는 경우 (OOP의 주요 이점 중 하나입니까?)이 질문은 매우 자연스럽고 명백한 필요성입니다.
Carl Meyer

82
@ S.Lott이 경우 "내가 문맥을 이해하고 있는지 잘 모르겠습니다. 사용 사례를 설명해 주시겠습니까?"와 같이 무례하지 않은 버전을 자유롭게 연습하십시오.
Carl Meyer

답변:


84

( 업데이트 : 따라서 상속 계층 구조 아래로 역 OneToOneField 관계를 통해 쿼리를 select_related (및 따를 수 있습니다 장고 1.2 및 최신)의 경우, 추가 요구하지 않는 사용할 수있는 더 좋은 방법이 있어요 real_type상위 모델에 필드를 그것으로 사용할 수 있습니다. InheritanceManagerdjango-model-utils 프로젝트.)

이를 수행하는 일반적인 방법은 적절한 "리프"클래스의 컨텐츠 유형을 저장하는 Parent 모델의 ContentType에 ForeignKey를 추가하는 것입니다. 이것이 없으면 상속 트리의 크기에 따라 인스턴스를 찾기 위해 자식 테이블에 대해 상당히 많은 쿼리를 수행해야 할 수 있습니다. 한 프로젝트에서 수행 한 방법은 다음과 같습니다.

from django.contrib.contenttypes.models import ContentType
from django.db import models

class InheritanceCastModel(models.Model):
    """
    An abstract base class that provides a ``real_type`` FK to ContentType.

    For use in trees of inherited models, to be able to downcast
    parent instances to their child types.

    """
    real_type = models.ForeignKey(ContentType, editable=False)

    def save(self, *args, **kwargs):
        if self._state.adding:
            self.real_type = self._get_real_type()
        super(InheritanceCastModel, self).save(*args, **kwargs)
    
    def _get_real_type(self):
        return ContentType.objects.get_for_model(type(self))
            
    def cast(self):
        return self.real_type.get_object_for_this_type(pk=self.pk)
    
    class Meta:
        abstract = True

이것은 재사용이 가능하도록 추상 기본 클래스로 구현됩니다. 이러한 메서드와 FK를 특정 상속 계층 구조의 부모 클래스에 직접 넣을 수도 있습니다.

이 솔루션은 상위 모델을 수정할 수없는 경우 작동하지 않습니다. 이 경우 모든 하위 클래스를 수동으로 확인하는 데 거의 어려움이 있습니다.


감사합니다. 이것은 아름답고 확실히 시간을 절약했습니다.
Spike

이것은 매우 도움이되었지만 null = True로 FK를 정의하려는 이유가 궁금합니다. 우리는 코드를 거의 그대로 복사했으며 FK가 필수 인 경우 쉽게 감지되고 해결 될 수있는 버그에 의해 조금씩 복사되었습니다 (cast () 메서드가이를 필수로 취급한다는 점에 유의하십시오).
Shai Berger

1
@ShaiBerger 훌륭한 질문입니다. 3 년이 지난 지금, 원래 그렇게 된 이유를 모르겠습니다. :-) null = True를 제거하기위한 편집.
Carl Meyer

2
@sunprophit 내 응답을 더주의 깊게 다시 읽어야합니다. 예, 물론 필드를 self.child_object()사용하여 수행 할 수 있으며 real_type(즉, 위의 코드에서 cast()메서드로) 한 인스턴스에 대해 하나의 쿼리 만 사용합니다. 그러나 인스턴스로 가득 찬 쿼리 세트가있는 경우 N 쿼리가됩니다. 단일 쿼리에서 개체의 전체 쿼리 집합에 대한 하위 클래스 데이터를 가져 오는 유일한 방법은 InheritanceManager가 수행하는 조인을 사용하는 것입니다.
Carl Meyer

1
내 실수, 부끄러워. 알아 채고 고쳐 주셔서 감사합니다.
Antoine Pinsard 2010 년

23

Python에서 ( "new-style") 클래스 X가 주어지면 X.__subclasses__()클래스 객체 목록을 반환 하는를 사용하여 (직접) 하위 클래스를 가져올 수 있습니다 . (당신이 "더 많은 자손"을 원한다면, 당신은 또한 __subclasses__각각의 직접적인 서브 클래스 등 을 호출 해야 할 것입니다. 파이썬에서 효과적으로하는 방법에 대한 도움이 필요하다면 그냥 물어보세요!)

어떤 식 으로든 관심있는 하위 클래스를 식별하면 (아마도 모든 하위 클래스의 인스턴스를 원하는 경우 모두) getattr(parentclass,'%s_set' % childclass.__name__)도움이 될 것입니다 (하위 클래스의 이름이 인 'foo'경우 액세스하는 것과 같습니다. parentclass.foo_set더 이상도 그 이하도 아닙니다. ). 다시 말하지만, 설명이나 예가 필요하면 물어보세요!


1
이것은 훌륭한 정보입니다 ( 하위 클래스 에 대해 몰랐습니다 ).하지만 질문은 실제로 Django 모델이 상속을 구현하는 방법에 훨씬 더 구체적이라고 생각합니다. "Parent"테이블을 쿼리하면 Parent의 인스턴스가 반환됩니다. 이 수 사실 SomeChild의 인스턴스 수 있지만 장고 (비싼 수) 자동으로 알아낼 수 없습니다. Parent 인스턴스의 속성을 통해 SomeChild 인스턴스에 도달 할 수 있지만 Parent의 다른 하위 클래스와 달리 원하는 SomeChild임을 이미 알고있는 경우에만 가능합니다.
Carl Meyer

2
죄송합니다 . " 사실 SomeChild의 인스턴스 일 수 있습니다." 라고 말할 때 명확하지 않습니다 . 가지고있는 객체는 Python의 Parent 인스턴스이지만 SomeChild 테이블에 관련 항목이있을 수 있습니다. 즉, SomeChild 인스턴스로 작업하는 것을 선호 할 수 있습니다.
Carl Meyer

재밌 네요 ... 당시에는이 특정 정보가 필요하지 않았지만 다른 문제에 대해 생각하고 있었는데이게 정확히 필요한 것으로 판명 되었으니 다시 한 번 감사드립니다!
Gabriel Hurley

이것은 진짜 대답입니다. 장고는 결국 파이썬 일뿐입니다.
snakesNbronies

5

Carl의 솔루션은 좋은 솔루션입니다. 관련된 하위 클래스가 여러 개인 경우 수동으로 수행하는 한 가지 방법이 있습니다.

def get_children(self):
    rel_objs = self._meta.get_all_related_objects()
    return [getattr(self, x.get_accessor_name()) for x in rel_objs if x.model != type(self)]

장고가 발전함에 따라 안정적이라고 보장 할 수없는 _meta의 함수를 사용하지만 트릭을 수행하고 필요하면 즉석에서 사용할 수 있습니다.



5

이를 위해 django-polymorphic 을 사용할 수 있습니다 .

파생 클래스를 실제 형식으로 다시 자동 캐스팅 할 수 있습니다. 또한 Django 관리자 지원,보다 효율적인 SQL 쿼리 처리, 프록시 모델, 인라인 및 폼셋 지원을 제공합니다.

기본 원칙은 여러 번 재창조 된 것 같습니다 (Wagtail .specific또는이 게시물에 설명 된 예제 포함 ). 그러나 N 쿼리 문제가 발생하지 않도록하거나 관리자, 양식 집합 / 인라인 또는 타사 앱과 잘 통합되도록하려면 더 많은 노력이 필요합니다.


1
이건 좋아 보이고 나는 그것을 시도하고 싶다. 마이그레이션 할 때 polymorphic_ctype 필드를 적절한 콘텐츠 유형으로 채우면 충분합니까 (남쪽 마이그레이션에서)?
조슈아

1
@joshua : 네, 그게 당신이해야 할 유일한 일입니다.
vdboor

django-model-utils에서 취한 접근 방식과의 차이점을 설명하여 답변을 약간 구체화 할 수 있습니다 (Carl Meyer의 답변 참조).
Ryne Everett 2016 년

1
수락 된 답변이 오래되었습니다. 현재 이것이 최선의 답변입니다.
Pierre.Sassoulas

2

여기 내 솔루션이 있습니다. 다시 사용 _meta하므로 안정적이라는 보장은 없습니다.

class Animal(models.model):
    name = models.CharField()
    number_legs = models.IntegerField()
    ...

    def get_child_animal(self):
        child_animal = None
        for r in self._meta.get_all_related_objects():
            if r.field.name == 'animal_ptr':
                child_animal = getattr(self, r.get_accessor_name())
        if not child_animal:
            raise Exception("No subclass, you shouldn't create Animals directly")
        return child_animal

class Dog(Animal):
    ...

for a in Animal.objects.all():
    a.get_child_animal() # returns the dog (or whatever) instance

0

django.db.models.fields.related.RelatedManager의 인스턴스 인 부모의 모든 필드를 검색하여이를 달성 할 수 있습니다. 귀하의 예에서 말하는 하위 클래스가 하위 클래스가 아닌 것 같습니다. 권리?


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.