在 Django 中创建自定义连接
Creating Custom Join in Django
我正在努力在 Django 中创建正确的预取行为。这是问题的概要:
- 每个账户都有 DailyQuotes,每天在不同的时间更新(想想快照)
- 需要查询所有这些 DailyQuotes,并且只获取每个账户的最新报价
以下是模型:
class Account(models.Model):
name = models.TextField(default="")
...
class DailyQuotes(models.Model):
account = models.ForeignKey(Account, related_name="quote", on_delete=models.CASCADE)
date = models.DateField(default=None)
...
目前我视图中的查询如下所示:
acc_ids = [1,2,3]
max_date = DailyQuotes.objects.aggregate(Max("date"))["date__max"]
accounts = (
Account.objects.filter(id__in=acc_ids)
.prefetch_related(
Prefetch(
"quote",
queryset=DailyQuotes.objects.filter(date=date),
),
)
)
# Feed into serializer, etc
这有效并生成 3 个查询:1 个用于最大日期,1 个用于帐户,1 个用于报价。此解决方案的问题在于,如果一个帐户有更多最新的 DailyQuotes,那么其他帐户将 return 没有报价。所以我需要根据每个帐户的最大日期获取最新的 DailyQuotes,而不是所有帐户。
我已经生成了执行我想要的操作的 SQL 查询,但是将其转换为 Django 代码一直给我带来问题。我可以执行原始 SQL 但我想将其保留在 Django 中。这是当前 SQL 的样子以及它需要的样子:
当前预取查询(由 Django 生成):
SELECT ... FROM dailyquotes
WHERE (dailyquotes.date = 2022-05-05
AND dailyquotes.account_id IN (1,2,3))
所需的预取查询(或类似查询):
SELECT ... FROM dailyquotes dq
JOIN (SELECT account_id, MAX(date) AS date__max FROM dailyquotes
WHERE account_id in (1,2,3) group by account_id) dates
ON dq.account_id = dates.account_id AND dq.date = dates.date__max
如有任何帮助,我们将不胜感激!
编辑:
在 SamSparx 的帮助下,我得出以下结论:
acc_ids = [1,2,3]
max_dates = (DailyQuotes.objects.filter(account_id__in=acc_ids)
.values("account_id")
.annotate(max_date=Max("date")))
recordsets = None
# get the recordsets
for max_date in max_dates:
qs = DailyQuotes.objects.filter(
account_id=max_date["account_id"],
date=max_date["max_date"])
if recordsets is None:
recordsets = qs
else:
recordsets = recordsets | qs
accounts = (Account.objects.filter(
id__in=permissions["acc_ids"].values())
.prefetch_related(
Prefetch(
"quote",
queryset=recordsets,
),
)
)
如果你没有绑定到 prefetch_related,你可以在 Django 中通过 DailyQuotes 在 2 个调用中完成 - 1 个用于收集最大日期,1 个用于最终记录集(即使使用 select_related 如果你需要随附的帐户信息)。
from django.db.models import Max
#define lists
acc_ids = [0,1,2]
max_dates = []
recordsets = []
final_recordset = []
#get the max date for each account ID
max_dates = DailyQuotes.objects.filter(account_id__in=acc_ids).values('account_id').annotate(max_date = Max('date'))
#get the recordsets
for max_date in max_dates:
qs = DailyQuotes.objects.filter(account_id = max_date['account_id'], date = max_date['max_date'] )
#qs = DailyQuotes.objects.filter(account_id = max_date['account_id'], date = max_date['max_date']).select_related('account') if you need associated account info
recordsets.append(qs)
#combine the recordsets for serialising - you may want to modify this based on length of recordsets list (in case of empty accounts) for robustness
final_recordset = recordsets[0].union( recordsets[1], recordsets[2])
我正在努力在 Django 中创建正确的预取行为。这是问题的概要:
- 每个账户都有 DailyQuotes,每天在不同的时间更新(想想快照)
- 需要查询所有这些 DailyQuotes,并且只获取每个账户的最新报价
以下是模型:
class Account(models.Model):
name = models.TextField(default="")
...
class DailyQuotes(models.Model):
account = models.ForeignKey(Account, related_name="quote", on_delete=models.CASCADE)
date = models.DateField(default=None)
...
目前我视图中的查询如下所示:
acc_ids = [1,2,3]
max_date = DailyQuotes.objects.aggregate(Max("date"))["date__max"]
accounts = (
Account.objects.filter(id__in=acc_ids)
.prefetch_related(
Prefetch(
"quote",
queryset=DailyQuotes.objects.filter(date=date),
),
)
)
# Feed into serializer, etc
这有效并生成 3 个查询:1 个用于最大日期,1 个用于帐户,1 个用于报价。此解决方案的问题在于,如果一个帐户有更多最新的 DailyQuotes,那么其他帐户将 return 没有报价。所以我需要根据每个帐户的最大日期获取最新的 DailyQuotes,而不是所有帐户。
我已经生成了执行我想要的操作的 SQL 查询,但是将其转换为 Django 代码一直给我带来问题。我可以执行原始 SQL 但我想将其保留在 Django 中。这是当前 SQL 的样子以及它需要的样子:
当前预取查询(由 Django 生成):
SELECT ... FROM dailyquotes
WHERE (dailyquotes.date = 2022-05-05
AND dailyquotes.account_id IN (1,2,3))
所需的预取查询(或类似查询):
SELECT ... FROM dailyquotes dq
JOIN (SELECT account_id, MAX(date) AS date__max FROM dailyquotes
WHERE account_id in (1,2,3) group by account_id) dates
ON dq.account_id = dates.account_id AND dq.date = dates.date__max
如有任何帮助,我们将不胜感激!
编辑:
在 SamSparx 的帮助下,我得出以下结论:
acc_ids = [1,2,3]
max_dates = (DailyQuotes.objects.filter(account_id__in=acc_ids)
.values("account_id")
.annotate(max_date=Max("date")))
recordsets = None
# get the recordsets
for max_date in max_dates:
qs = DailyQuotes.objects.filter(
account_id=max_date["account_id"],
date=max_date["max_date"])
if recordsets is None:
recordsets = qs
else:
recordsets = recordsets | qs
accounts = (Account.objects.filter(
id__in=permissions["acc_ids"].values())
.prefetch_related(
Prefetch(
"quote",
queryset=recordsets,
),
)
)
如果你没有绑定到 prefetch_related,你可以在 Django 中通过 DailyQuotes 在 2 个调用中完成 - 1 个用于收集最大日期,1 个用于最终记录集(即使使用 select_related 如果你需要随附的帐户信息)。
from django.db.models import Max
#define lists
acc_ids = [0,1,2]
max_dates = []
recordsets = []
final_recordset = []
#get the max date for each account ID
max_dates = DailyQuotes.objects.filter(account_id__in=acc_ids).values('account_id').annotate(max_date = Max('date'))
#get the recordsets
for max_date in max_dates:
qs = DailyQuotes.objects.filter(account_id = max_date['account_id'], date = max_date['max_date'] )
#qs = DailyQuotes.objects.filter(account_id = max_date['account_id'], date = max_date['max_date']).select_related('account') if you need associated account info
recordsets.append(qs)
#combine the recordsets for serialising - you may want to modify this based on length of recordsets list (in case of empty accounts) for robustness
final_recordset = recordsets[0].union( recordsets[1], recordsets[2])