Django 中的依赖子查询
Dependant subqueries in Django
我一直在用头撞墙尝试使用 Django ORM 进行此查询,我到处搜索但找不到答案。
这是我的模型。
class Decision(models.Model):
ACTION_CHOICES = [('include', _('Include')), ('exclude', _('Exclude'))]
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
date_taken = models.DateTimeField(_('date taken'), default=timezone.now)
action = models.CharField(max_length=7, choices=ACTION_CHOICES)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
project = models.ForeignKey('projects.Project', on_delete=models.CASCADE)
is_permanent = models.BooleanField(_('is permanent'), default=False)
我想查询的是用户在项目中所做的每个 content_object 的最新永久决定,因为在应用程序中重复调用此查询,迭代更简单的 QuerySet,如 Decision.filter(user_id=user.pk, project_id=project.pk)
以获得最终结果,这不是一个选项。到目前为止,我使用像这样的 RawQuerySet 解决了它。
Decision.objects.raw("SELECT decision.id, decision.content_type_id, decision.object_id "
"FROM canvasblocks_decision AS decision "
"WHERE decision.date_taken = (SELECT max(last_decision.date_taken) "
" FROM canvasblocks_decision AS last_decision "
" WHERE last_decision.object_id = decision.object_id AND "
" last_decision.content_type_id = decision.content_type_id AND "
" last_decision.project_id = %s AND decision.user_id = %s) ",
[project.pk, user.pk])
但我不喜欢这个解决方案,因为我失去了所有 Django ORM 的能力,而且由于我必须再次过滤此查询以检查操作,失去这种能力很痛苦,因为我必须在子查询上转换它随之而来的所有复杂性。伙计们,你们知道更好的方法吗?顺便说一句,我正在使用 Django 1.9.4。提前致谢。
如果数据集很小,您可以进行多次传递,但这不是一个好主意。
几个选项,具体取决于您使用的数据库。
1) 您可以将 sql 更改为使用排名或 dense_rank 函数来简化查询。
select decision.id, decision.content_type_id, decision.object_id,
first_value (last_decision.date_taken)
over (partition by ecision.id, decision.content_type_id, decision.object_id
order by last_decision.date_taken desc
)
from canvasblocks_decision AS decision
...
2) 您可以将相同的逻辑放在注释中,以获得排名。这样你就拥有了你的 django 对象提供的一切,并且你得到了这个额外的列。
from django.db.models.expressions import RawSQL
Decision.objects.filter().annotate(rank=RawSQL("RANK() OVER (partition by id, content_type, object_id
(ORDER BY date_taken DESC)", [])
)
..
这可能有帮助:
我一直在用头撞墙尝试使用 Django ORM 进行此查询,我到处搜索但找不到答案。 这是我的模型。
class Decision(models.Model):
ACTION_CHOICES = [('include', _('Include')), ('exclude', _('Exclude'))]
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
date_taken = models.DateTimeField(_('date taken'), default=timezone.now)
action = models.CharField(max_length=7, choices=ACTION_CHOICES)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
project = models.ForeignKey('projects.Project', on_delete=models.CASCADE)
is_permanent = models.BooleanField(_('is permanent'), default=False)
我想查询的是用户在项目中所做的每个 content_object 的最新永久决定,因为在应用程序中重复调用此查询,迭代更简单的 QuerySet,如 Decision.filter(user_id=user.pk, project_id=project.pk)
以获得最终结果,这不是一个选项。到目前为止,我使用像这样的 RawQuerySet 解决了它。
Decision.objects.raw("SELECT decision.id, decision.content_type_id, decision.object_id "
"FROM canvasblocks_decision AS decision "
"WHERE decision.date_taken = (SELECT max(last_decision.date_taken) "
" FROM canvasblocks_decision AS last_decision "
" WHERE last_decision.object_id = decision.object_id AND "
" last_decision.content_type_id = decision.content_type_id AND "
" last_decision.project_id = %s AND decision.user_id = %s) ",
[project.pk, user.pk])
但我不喜欢这个解决方案,因为我失去了所有 Django ORM 的能力,而且由于我必须再次过滤此查询以检查操作,失去这种能力很痛苦,因为我必须在子查询上转换它随之而来的所有复杂性。伙计们,你们知道更好的方法吗?顺便说一句,我正在使用 Django 1.9.4。提前致谢。
如果数据集很小,您可以进行多次传递,但这不是一个好主意。
几个选项,具体取决于您使用的数据库。
1) 您可以将 sql 更改为使用排名或 dense_rank 函数来简化查询。
select decision.id, decision.content_type_id, decision.object_id,
first_value (last_decision.date_taken)
over (partition by ecision.id, decision.content_type_id, decision.object_id
order by last_decision.date_taken desc
)
from canvasblocks_decision AS decision
...
2) 您可以将相同的逻辑放在注释中,以获得排名。这样你就拥有了你的 django 对象提供的一切,并且你得到了这个额外的列。
from django.db.models.expressions import RawSQL
Decision.objects.filter().annotate(rank=RawSQL("RANK() OVER (partition by id, content_type, object_id
(ORDER BY date_taken DESC)", [])
)
..
这可能有帮助: