在 Django ORM 中加入子查询 table
Join with Subquery table in Django ORM
假设我有一个名为 Book 的模型和另一个名为 Reaction 的模型,它具有指向 Book[= 的外键36=] 和一个名为 outcome
的字符串字段。数据库中有大量书籍和反应行。我想使用 Django ORM 查询数据库,结果如下 SQL:
SELECT b.*, r.n_like, r.n_dislike
FROM book b
JOIN (
SELECT
book_id,
COALESCE(SUM(CASE WHEN outcome='like' THEN 1 ELSE 0 END), 0) n_like,
COALESCE(SUM(CASE WHEN outcome='dislike' THEN 1 ELSE 0 END), 0) n_dislike
FROM reaction
GROUP BY book_id) r ON b.id=r.book_id;
我该怎么做?
请注意,此查询 returns 仅包含至少有一种反应及其对应的 like 和 dislike 数量的图书反应。
我知道如何在 Django 中使用 Case
、When
、Sum
和 Coalesce
。我也知道如何编写在 SELECT
子句中生成这些注释的查询(我尝试过这种查询,但速度很慢)。我的问题仅仅是关于使用 Django 模型加入和选择子查询。子查询是这样的:
reactions_query = Subquery(
Reaction.objects
.values('book_id')
.annotate(n_like=Coalesce(Sum(Case(When(outcome='like', then=1), default=0, output_field=IntegerField())), 0))
.annotate(n_dislike=Coalesce(Sum(Case(When(outcome='dislike', then=1), default=0, output_field=IntegerField())), 0))
.values('book_id', 'n_like', 'n_dislike')
)
Book.objects.filter(...) <-- how?
编辑:这些不是我的实际模型。我简化了它们。无论如何,这就是它们的样子:
class Book(models.Model):
name = models.CharField(max_length=100)
pub_date = models.CharField(max_length=100)
about = models.TextField()
class Reaction(models.Model):
LIKE = 'like'
DISLIKE = 'dislike'
OUTCOME_CHOICES = ((LIKE, ':)'), (DISLIKE, ':('))
book = models.ForeignKey(Book, on_delete=models.CASCADE)
outcome = models.CharField(max_length=20, choices=OUTCOME_OPTIONS, null=True)
您可以使用:
from django.db.models import <strong>Count, Q, Value</strong>
from django.db.models.functions import Coalesce
Book.objects.filter(
<strong>reaction__isnull=False</strong>
).annotate(
n_like=Coalesce(Count('reaction', filter=Q(outcome='like')), Value(0)),
n_dislike=Coalesce(Count('reaction', filter=Q(outcome='dislike')), Value(0))
)
.filter(<b>reaction__isnull=False</b>)
将阻止包含 Book
s 而没有任何反应。因此,这将创建一个带有 INNER JOIN
.
的查询
假设我有一个名为 Book 的模型和另一个名为 Reaction 的模型,它具有指向 Book[= 的外键36=] 和一个名为 outcome
的字符串字段。数据库中有大量书籍和反应行。我想使用 Django ORM 查询数据库,结果如下 SQL:
SELECT b.*, r.n_like, r.n_dislike
FROM book b
JOIN (
SELECT
book_id,
COALESCE(SUM(CASE WHEN outcome='like' THEN 1 ELSE 0 END), 0) n_like,
COALESCE(SUM(CASE WHEN outcome='dislike' THEN 1 ELSE 0 END), 0) n_dislike
FROM reaction
GROUP BY book_id) r ON b.id=r.book_id;
我该怎么做?
请注意,此查询 returns 仅包含至少有一种反应及其对应的 like 和 dislike 数量的图书反应。
我知道如何在 Django 中使用 Case
、When
、Sum
和 Coalesce
。我也知道如何编写在 SELECT
子句中生成这些注释的查询(我尝试过这种查询,但速度很慢)。我的问题仅仅是关于使用 Django 模型加入和选择子查询。子查询是这样的:
reactions_query = Subquery(
Reaction.objects
.values('book_id')
.annotate(n_like=Coalesce(Sum(Case(When(outcome='like', then=1), default=0, output_field=IntegerField())), 0))
.annotate(n_dislike=Coalesce(Sum(Case(When(outcome='dislike', then=1), default=0, output_field=IntegerField())), 0))
.values('book_id', 'n_like', 'n_dislike')
)
Book.objects.filter(...) <-- how?
编辑:这些不是我的实际模型。我简化了它们。无论如何,这就是它们的样子:
class Book(models.Model):
name = models.CharField(max_length=100)
pub_date = models.CharField(max_length=100)
about = models.TextField()
class Reaction(models.Model):
LIKE = 'like'
DISLIKE = 'dislike'
OUTCOME_CHOICES = ((LIKE, ':)'), (DISLIKE, ':('))
book = models.ForeignKey(Book, on_delete=models.CASCADE)
outcome = models.CharField(max_length=20, choices=OUTCOME_OPTIONS, null=True)
您可以使用:
from django.db.models import <strong>Count, Q, Value</strong>
from django.db.models.functions import Coalesce
Book.objects.filter(
<strong>reaction__isnull=False</strong>
).annotate(
n_like=Coalesce(Count('reaction', filter=Q(outcome='like')), Value(0)),
n_dislike=Coalesce(Count('reaction', filter=Q(outcome='dislike')), Value(0))
)
.filter(<b>reaction__isnull=False</b>)
将阻止包含 Book
s 而没有任何反应。因此,这将创建一个带有 INNER JOIN
.