Как объединить три модели Django Models для получения набора запросов?
Я хочу создать набор запросов, который ссылается на три связанные модели и позволяет мне фильтровать. SQL может выглядеть следующим образом:
SELECT th.id, th.customer, ft.filename, fva.path
FROM TransactionHistory th
LEFT JOIN FileTrack ft
ON th.InboundFileTrackID = ft.id
LEFT JOIN FileViewArchive fva
ON fva.FileTrackId = ft.id
WHERE th.customer = 'ACME, Inc.'
-- AND ft.filename like '%storage%' --currently don't need to do this, but seeing placeholder logic would be nice
У меня есть три модели в Django, показанные ниже. Это немного сложно, потому что модель TransactionHistory имеет два внешних ключа к одной и той же модели (FileTrack). А FileViewArchive имеет внешний ключ к FileTrack.
class FileTrack(models.Model):
id = models.BigIntegerField(db_column="id", primary_key=True)
filename = models.CharField(db_column="filename", max_length=128)
class Meta:
managed = False
db_table = "FileTrack"
class TransactionHistory(models.Model):
id = models.BigIntegerField(db_column="id", primary_key=True)
customer = models.CharField(db_column="Customer", max_length=128)
inbound_file_track = models.ForeignKey(
FileTrack,
db_column="InboundFileTrackId",
related_name="inbound_file_track_id",
on_delete=models.DO_NOTHING,
null=True,
)
outbound_file_track = models.ForeignKey(
FileTrack,
db_column="OutboundFileTrackId",
related_name="outbound_file_track_id",
on_delete=models.DO_NOTHING,
null=True,
)
class Meta:
managed = False
db_table = "TransactionHistory"
class FileViewArchive(models.Model):
id = models.BigIntegerField(db_column="id", primary_key=True)
file_track = models.ForeignKey(
FileTrack,
db_column="FileTrackId",
related_name="file_track_id",
on_delete=models.DO_NOTHING,
null=True,
)
path = models.CharField(db_column="Path", max_length=256)
class Meta:
managed = False
db_table = "FileViewArchive"
Одна вещь, которую я попробовал:
qs1 = TransactionHistory.objects.select_related('inbound_file_track').filter(customer='ACME, Inc.')
qs2 = FileViewArchive.objects.select_related('file_track').all()
qs = qs1 & qs2 # doesn't work b/c they are different base models
И эта идея использовать цепочку тоже не работает, потому что она посылает два отдельных запроса, и я не совсем уверен, объединяет ли она их. Мне нужен один запрос, чтобы быть более производительным. Также он возвращает итерабельность, поэтому я не уверен, что смогу использовать это в своем представлении (Django Rest Framework). Наконец, x ниже возвращает объект TransactionHistory, так что я даже не могу получить доступ к полям из двух других моделей.
from itertools import chain
c = chain(qs1 | qs2) # great that his this lazy and doesn't evaluate until used!
type(c) # this returns <class 'itertools.chain'> and it doesn't consolidate
x = list(c)[0] # runs two separate queries
type(x) # a TransactionHistory object -> so no access to the Filetrack or FileViewArchive fields
Есть идеи, как я могу соединить три модели вместе? Что-то вроде этого?:
qs = TransactionHistory.objects.select_related('inbound_file_track').select_related('file_track').filter(customer='ACME, Inc.', file_track__filename__contains='storge')
Исходя из sql-запроса, которым вы поделились, вы фильтруете на основе имени файла inbound_file_track. Поэтому должно работать что-то вроде этого:
TransactionHistory.objects.select_related(
'inbound_file_track',
).prefetch_related(
'inbound_file_track__file_track_id',
).filter(
customer='ACME, Inc.', inbound_file_track___filename__contains='storage',
)