Django에서 부모 클래스와 그로부터 상속되는 여러 자식 클래스가있는 경우 일반적으로 parentclass.childclass1_set 또는 parentclass.childclass2_set을 통해 자식에 액세스하지만 원하는 특정 자식 클래스의 이름을 모르는 경우 어떻게해야합니까?
자식 클래스 이름을 몰라도 부모-> 자식 방향으로 관련 개체를 가져 오는 방법이 있습니까?
Django에서 부모 클래스와 그로부터 상속되는 여러 자식 클래스가있는 경우 일반적으로 parentclass.childclass1_set 또는 parentclass.childclass2_set을 통해 자식에 액세스하지만 원하는 특정 자식 클래스의 이름을 모르는 경우 어떻게해야합니까?
자식 클래스 이름을 몰라도 부모-> 자식 방향으로 관련 개체를 가져 오는 방법이 있습니까?
답변:
( 업데이트 : 따라서 상속 계층 구조 아래로 역 OneToOneField 관계를 통해 쿼리를 select_related (및 따를 수 있습니다 장고 1.2 및 최신)의 경우, 추가 요구하지 않는 사용할 수있는 더 좋은 방법이 있어요 real_type
상위 모델에 필드를 그것으로 사용할 수 있습니다. InheritanceManager 에 django-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를 특정 상속 계층 구조의 부모 클래스에 직접 넣을 수도 있습니다.
이 솔루션은 상위 모델을 수정할 수없는 경우 작동하지 않습니다. 이 경우 모든 하위 클래스를 수동으로 확인하는 데 거의 어려움이 있습니다.
self.child_object()
사용하여 수행 할 수 있으며 real_type
(즉, 위의 코드에서 cast()
메서드로) 한 인스턴스에 대해 하나의 쿼리 만 사용합니다. 그러나 인스턴스로 가득 찬 쿼리 세트가있는 경우 N 쿼리가됩니다. 단일 쿼리에서 개체의 전체 쿼리 집합에 대한 하위 클래스 데이터를 가져 오는 유일한 방법은 InheritanceManager가 수행하는 조인을 사용하는 것입니다.
Python에서 ( "new-style") 클래스 X가 주어지면 X.__subclasses__()
클래스 객체 목록을 반환 하는를 사용하여 (직접) 하위 클래스를 가져올 수 있습니다 . (당신이 "더 많은 자손"을 원한다면, 당신은 또한 __subclasses__
각각의 직접적인 서브 클래스 등 을 호출 해야 할 것입니다. 파이썬에서 효과적으로하는 방법에 대한 도움이 필요하다면 그냥 물어보세요!)
어떤 식 으로든 관심있는 하위 클래스를 식별하면 (아마도 모든 하위 클래스의 인스턴스를 원하는 경우 모두) getattr(parentclass,'%s_set' % childclass.__name__)
도움이 될 것입니다 (하위 클래스의 이름이 인 'foo'
경우 액세스하는 것과 같습니다. parentclass.foo_set
더 이상도 그 이하도 아닙니다. ). 다시 말하지만, 설명이나 예가 필요하면 물어보세요!
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의 함수를 사용하지만 트릭을 수행하고 필요하면 즉석에서 사용할 수 있습니다.
내가 정말로 필요했던 것은 다음과 같다.
그것은 나를 위해 완벽하게 작동했습니다. 그래도 다른 모든 사람들에게 감사합니다. 나는 당신의 답변을 읽고 많은 것을 배웠습니다!
이를 위해 django-polymorphic 을 사용할 수 있습니다 .
파생 클래스를 실제 형식으로 다시 자동 캐스팅 할 수 있습니다. 또한 Django 관리자 지원,보다 효율적인 SQL 쿼리 처리, 프록시 모델, 인라인 및 폼셋 지원을 제공합니다.
기본 원칙은 여러 번 재창조 된 것 같습니다 (Wagtail .specific
또는이 게시물에 설명 된 예제 포함 ). 그러나 N 쿼리 문제가 발생하지 않도록하거나 관리자, 양식 집합 / 인라인 또는 타사 앱과 잘 통합되도록하려면 더 많은 노력이 필요합니다.
여기 내 솔루션이 있습니다. 다시 사용 _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