【问题标题】:Giving relations an order to sort by为关系排序
【发布时间】:2023-03-16 22:40:01
【问题描述】:

给定以下 Django 模型:

class Room(models.Model):
    name = models.CharField(max_length=20)

class Beacon(models.Model):
    room = models.ForeignKey(Room)
    uuid = models.UUIDField(default=uuid.uuid4)
    major = models.PostiveIntegerField(max_value=65536)
    minor = models.PositiveIntegerField(max_value=65536)

Beacon 模型是与房间的蓝牙信标关系。

我想选择与给定uuid, major, minor 组合匹配的所有房间。

关键是,我想通过离我最近的信标订购房间。因此,我需要能够为每个信标动态分配一个值,然后按它进行排序。

这可以用 Django ORM 实现吗?在 Django 1.8 中?

注意 - 我会事先知道信标的顺序,我将使用它们在查询字符串中传递的顺序。因此传递的第一个信标(uuid、major、minor)应该与 Room QuerySet 返回的第一个房间相匹配

我正在设想这样的事情,虽然我知道这行不通:

beacon_order = [
    beacon1 = 1,
    beacon0 = 2,
    beacon3 = 3,
]

queryset = Room.objects.annotate(beacon_order=beacon_order).\
               order_by('beacon_order')

【问题讨论】:

  • 通过注释可能是的。但是你会如何在这里定义 nearest 呢? “位置”存储在数据库中的什么位置?
  • 它将由传递给函数的参数的顺序决定。所以我会在查询时知道最近的一个,我只是不知道如何告诉 ORM 订单
  • 你的 beacon_order 是有序列表,还是带有键值对的字典?您现在拥有列表的方式不是有效的 Python 语法,所以我试图弄清楚它应该是什么意思。
  • beacon_order 可以是使注释工作所需的任何内容。

标签: django django-orm


【解决方案1】:

如果您已经知道信标的顺序,则无需在 QuerySet 本身内进行排序。获取一个名为beacon_list 的有序列表,其中按顺序包含信标的主键,例如索引 0 处的项目是最近的信标的主键,索引 1 处的项目是第二近的信标的 PK,等等。然后使用列表推导:

ordered_rooms = [Room.objects.get(pk=x) for x in beacon_list]

您也不必使用 PK,您可以使用任何标识数据库中给定对象的东西,例如name 字段。

【讨论】:

  • 这是非常不理想的。如果我通过了 500 个信标,您的方法将进行 500 个查询。使用注释,您可以在一个查询中完成所有操作
  • @synic Annotation 和 order_by 也不是便宜的操作。您可能想实际尝试比较性能,看看这样做有多大优势。
  • 此外,不可能参考 Python 代码中的某些内容对所有模型实例进行注释。只能使用简单值(例如常量)、对同一模型上的字段的引用或使用前两个类别中的一个或多个的聚合表达式(例如算术)进行注释。您必须过滤每个对象,然后对其进行注释,然后对所有对象进行排序。这不太可能比我在这里建议的方法具有更好的性能。
【解决方案2】:

看起来这样可行:

from django.db.models import Case, Q, When

beacons = request.query_params.getlist('beacon[]')
query = Q()
order = []

for pos, beacon in enumerate(beacons):
    uuid, major, minor = beacon.split(':')

    query |= Q(
        beacon__uuid=uuid,
        beacon__major=major,
        beacon__minor=minor,
        )

    order.append(When(
        beacon__uuid=uuid,
        beacon__major=major,
        beacon__minor=minor,
        then=pos,
        ))

rooms = Room.objects.filter(query).order_by(Case(*order))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-27
    • 1970-01-01
    • 1970-01-01
    • 2013-04-12
    • 1970-01-01
    • 2019-05-14
    • 2016-11-10
    • 1970-01-01
    相关资源
    最近更新 更多