如何将三个 Django 模型加入 return 一个查询集?

How can I join three Django Models to return a queryset?

我想创建一个引用三个相关模型的查询集,并允许我进行过滤。 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

并且 this idea 使用链也不起作用,因为它发送两个单独的查询,我不完全确定 if/how 它正在合并它们。我正在寻找一个查询以提高性能。它还 returns 一个可迭代的,所以我不确定我是否可以在我的视图中使用它(Django Rest Framework)。最后 returns 下面的 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='storage')

更多信息:这是如下所示的视图的一部分。它 returns 用作 Django Rest Framework 视图的一部分的查询集。

class Transaction(generics.ListAPIView):
    serializer_class = TransactionSerializer

    def filter_queryset(self, queryset):
        query_params = self.request.query_params.copy()
        company = query_params.pop("company", [])[0]
        filename = query_params.pop("filename", [])[0]

        # need code here that generate filtered queryset for filename and company
        # qs = TransactionHistory.objects.select_related('inbound_file_track').select_related('file_track').filter(customer='ACME, Inc.', file_track__filename__contains='storage')

        return qs.order_by("id")

根据您共享的 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',
)