Определение типов экземпляров модели Django после запроса к базовому классу - PullRequest
10 голосов
/ 08 марта 2011

Есть ли способ определить, что такое "настоящий" класс объекта базы данных Django после того, как он был возвращен из запроса для базового класса?

Например, если у меня есть эти модели ...

class Animal(models.Model):
    name= models.CharField(max_length=128)

class Person(Animal):
    pants_size = models.IntegerField(null=True)

class Dog(Animal):
    panting_rate = models.IntegerField(null=True)

И создать эти экземпляры ...

Person(name='Dave').save()
Dog(name='Mr. Rufflesworth').save()

Если я выполняю запрос, подобный Animal.objects.all(), я получаю два экземпляра Animal, а не экземпляр Person и экземпляр Dog. Есть ли способ определить, какой экземпляр какого типа?


К вашему сведению: я уже пытался это сделать ...

isinstance(Animal.objects.get(name='Dave'),Person) # <-- Returns false!

Но, похоже, это не сработает.

Ответы [ 4 ]

17 голосов
/ 09 ноября 2012

Мы реализовали нашу собственную функцию cast (), которая работает довольно хорошо (без ContentType's):

class Base(models.Model):
    """
    If your class needs the basics, like created date, modified date etc, then
    inherit from this Base class.
    """
    created = models.DateTimeField(_('Created'), auto_now_add=True)
    modified = models.DateTimeField(_('Modified'), auto_now=True)

    class Meta:
        abstract = True

    def __str__(self):
        return '%s [%s]' % (self.__class__.__name__, self.id)

    def get_class_name(self):
        return str(self.__class__.__name__).lower()

    def to_json(self, include_related=True):
        return {
            'id': self.id,
            'created': self.created.isoformat(),
            'modified': self.modified.isoformat(),
            'class_name': self.__class__.__name__
        }

    def cast(self):
        """
        This method is quite handy, it converts "self" into its correct child class. For example:

        .. code-block:: python

           class Fruit(models.Model):
               name = models.CharField()

           class Apple(Fruit):
               pass

           fruit = Fruit.objects.get(name='Granny Smith')
           apple = fruit.cast()

        :return self: A casted child class of self
        """
        for name in dir(self):
            try:
                attr = getattr(self, name)
                if isinstance(attr, self.__class__):
                    return attr
            except:
                pass
        return self
10 голосов
/ 08 марта 2011

У меня была похожая проблема в прошлом, и в итоге я нашел удовлетворительное решение благодаря этому ответу .

Реализуя абстрактный класс, который хранит класс real и наследует его от вашего родительского класса, один раз можно привести каждый экземпляр родительского класса к фактическому типу. (Абстрактный класс, используемый в этом ответе, теперь доступен в django-model-utils .)

Например, после определения абстрактного класса (или еслиу вас есть django-model-utils), вы можете просто сделать:

class Animal(InheritanceCastModel):
    name= models.CharField(max_length=128)

class Person(Animal):
    pants_size = models.IntegerField(null=True)

class Dog(Animal):
    panting_rate = models.IntegerField(null=True)

Использовать его тривиально:

>>> from zoo.models import Animal, Person, Dog
>>> Animal(name='Malcolm').save()
>>> Person(name='Dave').save()
>>> Dog(name='Mr. Rufflesworth').save()
>>> for obj in Animal.objects.all():
...     print obj.name, type(obj.cast())
...
Malcolm <class 'zoo.models.Animal'>
Dave <class 'zoo.models.Person'>
Mr. Rufflesworth <class 'zoo.models.Dog'>
9 голосов
/ 08 марта 2011

Да, это можно сделать - вам просто нужно запросить автоматическое обратное соотношение OneToOneField.Например:

a = Animal.objects.select_related('person', 'dog')[0]
a = a.person or a.dog or a # whichever is not None
print a
print isinstance(a, Person)

Использование select_related здесь позволяет сделать это в одном запросе, вместо того, чтобы проверять наличие исключений DoesNotExist при доступе к атрибутам / отношениям подкласса.

См. Также мой ответ здесь и InheritanceManager в django-model-utils для более элегантного / долгосрочного решения.

Мыглядя на способы сделать это проще в ядре Джанго.

2 голосов
/ 21 мая 2013

Чтобы решить эту проблему, рассмотрите возможность использования django-polymorphic .Он поддерживает автоматическое понижение унаследованных моделей, работает с полями ForeignKeys / ManyToMany и также интегрируется в администратор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...