Подклассы модели Django: получить подкласс, запросив суперкласс - PullRequest
8 голосов
/ 24 июня 2010

Дан следующий код:

class BaseMedium(models.Model):
    title = models.CharField(max_length=40)
    slug = models.SlugField()

class A(BaseMedium):
    url = models.URLField()

class B(BaseMedium):
    email = models.EmailField()

Теперь я хочу запросить каждый BaseMedium.

b = BaseMedium.objects.all()

Как распечатать каждую информацию, включая поля подкласса, не зная, какой тип подкласса?

b[0].a будет печатать информацию, если b[0] на самом деле связано с A экземпляром, но если оно связано с B, то будет напечатано исключение DoesNotExist.

Это имеет смысл, но я хотел бы иметь общую переменную или метод, который возвращает связанный объект.

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

Я думал об использовании GenericForeignKey

class Generic(models.Model):
    basemedium = models.ForeignKey('BaseMedium')
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    object = generic.GenericForeignKey('content_type', 'object_id')

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

Ответы [ 3 ]

5 голосов
/ 13 апреля 2011

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

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

2 голосов
/ 24 июня 2010

Единственный способ сделать это - явно указать на базовой модели, какой это тип. Так что в BaseMedium есть поле derived_type (или что-то еще) и установите его на сохранение Тогда у вас может быть get_derived_type метод:

def get_derived_type(self):
    if self.derived_type ==  'A':
        return self.a
    elif self.derived_type == 'B':
        return self.b

и т. Д.

1 голос
/ 24 июня 2010

Спасибо, мистер. Роземан за ваш ответ. Я развил вашу идею немного дальше. Вот что я придумал:

def related_object(self, default_pointer_name='_ptr'):
        models = [A,B] #models
        object = None

        argument = '%s%s' %(self.__class__.__name__.lower(), default_pointer_name)
        query = { argument : self}

        for model in models:
            try:
                object = model.objects.get(**query)
            except model.DoesNotExist:
                pass
            else:
                return object

        if object == None:
            raise RelatedObjectException
        return object

Это метод, используемый BaseMedium.

...