Подтвердить что ты не робот

Как найти "конкретный класс" базового класса модели django

Я пытаюсь найти фактический класс объекта модели django при использовании наследования модели.

Некоторый код для описания проблемы:

class Base(models.model):
    def basemethod(self):
        ...

class Child_1(Base):
    pass

class Child_2(Base):
    pass

Если я создаю различные объекты двух классов Child и создаю набор запросов, содержащий их все:

Child_1().save()
Child_2().save()
(o1, o2) = Base.objects.all()

Я хочу определить, имеет ли объект тип Child_1 или Child_2 в basemethod, я могу перейти к дочернему объекту через o1.child_1 и o2.child_2, но это перехватит знания о дочерних классах в базовом классе.

Я придумал следующий код:

def concrete_instance(self):
    instance = None
    for subclass in self._meta.get_all_related_objects():
        acc_name = subclass.get_accessor_name()
        try:
            instance = self.__getattribute__(acc_name)
            return instance
        except Exception, e:
            pass

Но он чувствует себя хрупким, и я не уверен, что произойдет, если я наследую больше уровней.

4b9b3361

Ответ 1

Django реализует наследование модели с помощью OneToOneField между родительской моделью таблицы и дочерней моделью. Когда вы выполняете Base.object.all(), Django запрашивает только базовую таблицу и поэтому не знает, что такое дочерняя таблица. Поэтому, к сожалению, невозможно напрямую перейти к экземпляру дочерней модели без дополнительных запросов.

Этот snippet показывает общий метод добавления поля ContentType в базовую модель:

from django.contrib.contenttypes.models import ContentType

class Base(models.Model):
    content_type = models.ForeignKey(ContentType,editable=False,null=True)

    def save(self):
        if(not self.content_type):
            self.content_type = ContentType.objects.get_for_model(self.__class__)
        self.save_base()

    def as_leaf_class(self):
        content_type = self.content_type
        model = content_type.model_class()
        if(model == Base):
            return self
        return model.objects.get(id=self.id)

Затем вы можете сказать if Base.content_type.model_class(), чтобы определить тип.

Здесь - это еще один фрагмент, который добавляет пользовательский менеджер в микс.

Как вы можете видеть, оба этих решения могут быть чрезвычайно дорогими. Если у вас есть большое количество экземпляров, использование метода as_leaf_class() потребует одного запроса для каждого элемента.

Вместо этого, если у вас есть известный набор дочерних моделей, просто запросите каждую модель отдельно и соберите экземпляры в один список.

Ответ 2

Посмотрите на InheritanceManager в django-model-utils - привязав его к модели, вы получите конкретные дочерние классы (по крайней мере, на первом уровне):

from model_utils.managers import InheritanceManager

class Base(models.Model):
    objects = InheritanceManager()

# ...

Base.objects.all().select_subclasses() # returns instances of child classes

model-utils требует Django 1.2 или выше.

Ответ 3

Ну... Моя проблема была. С точки зрения, у меня была эта основная модель, скажем, "Big_Model" , и были некоторые "Small_Model", связанные с "Big_Model" . Поэтому, когда я хотел получить все "Small_Model", связанные с определенным экземпляром "Big_Model" , я сделал этот материал ** _ set.all(). Но дело в том, что Small_Model имеет дочерние классы, и я хотел, в views.py, получить, какой из дочерних классов был связан с экземплярами Small_Model. Мой трюк состоял в том, чтобы определить булевы методы в модели Small_Model, такие как is_child_1() и is_child_2(). И когда это правда, вы применяете фактический дочерний указатель вместо указателя Small_Model.

Хорошо... Это недостаточно ясно, но у меня нет много времени, чтобы написать хороший пример, поэтому я просто скопирую-вставьте мой случай здесь:

class Cache(models.Model):
  valor = models.DecimalField(max_digits=9, decimal_places=2, blank= True, null= True)
  evento=models.ForeignKey(Evento)
  def __unicode__(self):
    return u'%s: %s' % (self.evento, self.valor)
  class Meta:
    verbose_name='Cachê'
    verbose_name_plural='Cachês'
  def is_cb(self):
    try:
      self.cache_bilheteria
      return True
    except self.DoesNotExist:
      return False
  def is_co(self):
    try:
      self.cache_outro
      return True
    except self.DoesNotExist:
      return False

Ответ 4

Немного измененная версия того, что предложил Даниэль Нааб:

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

def ParentClass(models.Model):
    superclass = models.CharField(max_length = 255, blank = True)

    def save(self, *args, **kwargs):
        if not self.superclass:
            self.superclass = ContentType.objects.get_for_model(self.__class__)

        super(ParentClass, self).save(*args, **kwargs)

    def getChild(self):
        s = getattr(self, self.superclass)
        if hasattr(s, 'pk'):
            return s
        else:
            return None

class Child1(ParentClass):
    pass

class Child2(ParentClass):
    pass

Ответ 5

Он чувствует себя хрупким, потому что это так. (Это переиздание ответа в другом контексте. Программно программировать С++: можно ли это сделать?)

Читайте о полиморфизме. Почти каждая ситуация с "динамическим приложением" является примером полиморфизма, который предстоит реализовать.

Независимо от того, какое решение вы принимаете в динамическом приведении, уже сделано. Просто передайте реальную работу подклассам.

Вы оставили самую важную часть вашего примера. Полезная, полиморфная работа.

Когда вы сказали: "Я хочу определить, имеет ли объект тип Child_1 или Child_2...", вы упустили "так, чтобы я мог сделать объект aMethod() способом, уникальным для каждого подкласса". Этот метод является полезной работой, и он должен просто быть методом обоих подклассов.

class Base(models.model):
    def aMethod(self):
        # base class implementation.

class Child_1(Base):
    def aMethod(self):
        # Child_1 override of base class behavior.

class Child_2(Base):
    def aMethod(self):
        supert( Child_2, self ).aMethod() # Invoke the base class version
        # Child_2 extension to base class behavior.

Тот же метод, несколько реализаций. Никогда не нужно "идентификация типа времени выполнения" или определение конкретного класса.