复杂的Django自引用注解

0 投票
1 回答
1103 浏览
提问于 2025-04-17 23:37

这里有两个模型:

  • Floor(楼层),它有一个指向自己的引用,叫做 previousFloor,对于第一个 Floor 来说,这个值可以是 null
  • Room(房间),当然只能在一个 Floor 上(但一个 Floor 上可以有多个房间)。

我需要一个查询,返回所有在顶层的房间。


为了实现这个目标,我在 Floor 模型中创建了一个叫 get_next_floor 的函数,它会返回与当前楼层相连的下一个楼层,如果没有下一个楼层(也就是最后一个楼层),就返回 None。还有一个函数叫 is_last_floor,如果 get_next_floor 返回 None,它就返回 True,否则返回 False

最后,我会遍历所有的房间,并对每个房间所在的 Floor 调用 is_last_floor,以判断它是否符合我的条件。

这个问题可以通过在循环中保存已经找到的最后一个 Floor 来稍微优化一下。

不过,由于这只是我实际问题的一个抽象,而且我在处理一个大型数据库,这种方法因为循环的性能太差,已经不再可行。

有没有办法用一个查询和注解来实现这个?

1 个回答

1

一个楼层,如果是最高的那个Floor,那么肯定是没有其他的Floor把它当作previousFloor来引用。

所以:

top_floors = Floor.objects.exclude(id__in=Floor.objects.filter(previous_floor__isnull=False).values_list('previous_floor', flat=True))

这可以理解为:

SELECT "core_floor"."id", "core_floor"."name", "core_floor"."previous_floor_id" FROM "core_floor" WHERE NOT ("core_floor"."id" IN (SELECT U0."previous_floor_id" FROM "core_floor" U0 WHERE U0."previous_floor_id" IS NOT NULL))

不过,MySQL在处理嵌套查询时表现不是很好,所以可能更有效的方法是:

lower_floors = Floor.objects.filter(previous_floor__isnull=False).values_list('previous_floor', flat=True)
top_floors = Floor.objects.exclude(id__in=list(covered_floors))

可以查看这个链接了解更多信息:https://docs.djangoproject.com/en/dev/ref/models/querysets/#in

然后要获取顶层的房间:

Room.objects.filter(floor__in=top_floors)

撰写回答