Как я могу объединить несколько моделей в Django в виртуальную таблицу?
Если у меня есть 3 модели, например:
class Cow(models.Model):
name =
number_of_eyes =
number_of_feet =
color =
class Pig(models.Model):
name =
number_of_eyes =
number_of_feet =
intelligence =
class Horse(models.Model):
name =
number_of_eyes =
number_of_hooves =
weight_capacity =
speed =
И я заинтересован в создании одной Livestock
таблицы в моем шаблоне, которая имеет экземпляры всех 3, но меня интересуют только эти столбцы, которые есть у всех 3 моделей:
- имя
- количество_глаз
- количество_ног (количество_копыт, если лошадь)
И мы можем игнорировать все остальные столбцы.
Как я могу объединить их в один набор запросов?
Конечная цель - получить одну виртуальную таблицу (queryset), над которой я могу выполнить несколько других операций (filter, order_by, slice), а затем вернуть данные только в этих столбцах.
Возможно ли это в Django ORM?
Я думаю, у вас есть два варианта:
использование
itertools.chain
:from itertools import chain cows = Cow.objects.all() pigs = Pig.objects.all() horses = Horse.objects.all() livestock_list = sorted( chain(cows, pigs, horses), key=lambda livestock: livestock.created_at, reverse=True) )
использование
contenttypes
:from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey class Livestock(models.Model): content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') created = models.DateTimeField(auto_now_add=True) class Meta: ordering = ['-created']
Теперь вы можете запрашивать модель
Livestock
как любую другую модель вDjango
, но у вас может быть внешний ключ, который может ссылаться на n моделей. Вот что делаютcontenttypes
.Livestock.content_object
дает вам то, что вы хотите, в вашем случае это может бытьCow
,Pig
илиHorse
.Только не забудьте добавить объекты в модель
Livestock
после создания экземпляров лошади и т.д. Вам нужно добавить их в 2 модели на самом деле. Вы можете сделать это с помощью сигналов.
Я думаю, что второе решение лучше.
Очевидно, это также можно сделать с помощью Union
, как предложил Nick ODell:
from django.db.models import F
Cow.objects.filter(...).union(
Pig.objects.filter(...),
Horse.objects.filter(...).annotate(number_of_feet=F("number_of_hooves"))
).values('name', 'number_of_eyes', 'number_of_feet').order_by('name')[:3]
К сожалению, вы не можете фильтровать результирующий набор запросов после объединения, поэтому вам нужно фильтровать каждый набор запросов перед объединением, но в остальном все работает в моем быстром тестировании.
Насколько я понимаю, отличие от предложения MojixCoder использовать ContentType
заключается в том, что вам не нужно поддерживать отдельное определение этой виртуальной таблицы в модуле моделей Django. В некоторых случаях это может быть преимуществом, поскольку вам не нужно обновлять модуль при появлении новых моделей, которые вы хотите включить в свой запрос, но в других случаях это может быть недостатком, поскольку в моем способе приходится набирать много текста каждый раз, когда вы хотите использовать этот запрос, тогда как в примере MojixCoder вы определяете его один раз, и ваши запросы будут намного короче.