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

Как запросить объекты на основе абстрактного класса в Django?

Скажем, у меня есть абстрактный базовый класс, который выглядит так:

class StellarObject(BaseModel):
  title = models.CharField(max_length=255)
  description = models.TextField()
  slug = models.SlugField(blank=True, null=True)

  class Meta:
    abstract = True

Теперь скажем, что у меня есть два реальных класса базы данных, которые наследуются от StellarObject

class Planet(StellarObject):
  type = models.CharField(max_length=50)
  size = models.IntegerField(max_length=10)

class Star(StellarObject):
  mass = models.IntegerField(max_length=10)

До сих пор так хорошо. Если я хочу получить Планеты или Звезды, все, что я делаю, это:

Thing.objects.all() #or
Thing.objects.filter() #or count(), etc...

Но что, если я хочу получить ВСЕ StellarObjects? Если я это сделаю:

StellarObject.objects.all()

Он, конечно, возвращает ошибку, потому что абстрактный класс не является фактическим объектом базы данных и поэтому не может быть запрошен. Все, что я прочитал, говорит, что мне нужно сделать два запроса, по одному на планетах и ​​звездах, а затем объединить их. Это кажется ужасно неэффективным. Это единственный способ?

4b9b3361

Ответ 1

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

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

Ответ 2

Вы не можете запрашивать абстрактные базовые классы. Для наследования с несколькими таблицами вы можете использовать django-model-utils и InheritanceManager, который расширяет стандартный QuerySet с помощью метода select_subclasses(), который делает то, что вам нужно: он лево-присоединяет все наследуемые таблицы и возвращает соответствующий экземпляр типа для каждого строки.

Ответ 3

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

Ответ 4

В этом случае я думаю, что нет другого пути.

Для оптимизации вы можете избежать наследования от абстрактного StellarObject и использовать его как отдельную таблицу, подключенную через FK к объектам Star и Planet.

Таким образом, оба они имели бы то же самое. star.stellar_info.description.

Другим способом было бы добавить дополнительную модель для обработки информации и использовать StellarObject как through во многих отношениях.

Ответ 5

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

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

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

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

Ответ 6

Это кажется ужасно неэффективным. Это единственный способ?

Насколько я знаю, это единственный способ с Django ORM. Поскольку реализованные в настоящее время абстрактные классы - удобный механизм для абстрагирования общих атрибутов классов к суперклассам. ORM не предоставляет аналогичную абстракцию для запросов.

Вам будет лучше использовать другой механизм для реализации иерархии в базе данных. Один из способов сделать это - использовать одну таблицу и "тегировать" строки с использованием типа. Или вы можете реализовать общий внешний ключ для другой модели, которая содержит свойства (последнее не звучит правильно даже для меня).

Ответ 7

Это пример полиморфизма в ваших моделях (полиморф - много форм одного).

Вариант 1 - Если есть только одно место, вы имеете дело с этим:

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

Вариант 2 - Если вы делаете это довольно много или действительно требуете элегантности в синтаксисе запроса:

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

Вариант 3 - Если вы хотите получить половину дома:

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

Отметьте django-querysetsequence, который управляет объединением нескольких наборов запросов вместе.

Он не поддерживается так же стабильно, как django-polymorphic, но стоит упомянуть его.