Как мне найти «конкретный класс» базового класса модели Django? - PullRequest
11 голосов
/ 08 декабря 2008

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

Код для описания проблемы:

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

class Child_1(Base):
    pass

class Child_2(Base):
    pass

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

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

Я хочу определить, имеет ли объект тип Child_1 или Child_2 в базовом методе, я могу получить к дочернему объекту через 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

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

Ответы [ 5 ]

13 голосов
/ 08 декабря 2008

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

Этот фрагмент показывает общий метод добавления поля 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 () потребует один запрос для каждого элемента.

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

5 голосов
/ 12 декабря 2011

Посмотрите на 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

модель-утилита требует Django 1.2 или выше.

0 голосов
/ 03 июля 2013

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

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
0 голосов
/ 29 мая 2010

Ну ... моя проблема была. В представлении у меня была эта основная модель, скажем, «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
0 голосов
/ 08 декабря 2008

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

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

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

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

Когда вы сказали «Я хочу определить, относится ли объект к типу 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.

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

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