7

如何在 Django 中预取相关对象并按中间表中的字段对它们进行排序?

这是我正在使用的模型:

class Node(models.Model):
    name = models.CharField(max_length=255)
    edges = models.ManyToManyField('self', through='Edge', symmetrical=False)


class Edge(models.Model):
    from_node = models.ForeignKey(Node, related_name='from_node')
    to_node = models.ForeignKey(Node, related_name='to_node')

    weight = models.FloatField(default=0)

给定一个节点,我想预取所有相关节点,按重量排序。

当我使用这个查询时:

n = Node.objects.prefetch_related('to_node').order_by('edge__weight').get(name='x')

order_by 无效。

编辑:

到目前为止我最好的答案

n = Node.objects.get(name='x')
edges = Edge.objects.filter(from_node=n).prefetch_related('to_node').order_by('weight')

然后不是迭代n.edges(如我所愿),我迭代edges.to_node

4

3 回答 3

10

如今,您还可以使用 Prefetch 类来实现这一点:

https://docs.djangoproject.com/en/1.10/ref/models/querysets/#django.db.models.Prefetch

或者,如果您想始终默认执行此操作,您可以查看中间表上的元排序,例如:

class SomeThroughModel(models.Model):
    order = models.IntegerField("Order", default=0, blank=False, null=False)
    ...

    class Meta:
        ordering = ['order']  # order is the field holding the order
于 2017-03-30T18:44:07.090 回答
4

只是一个概念性的想法(凭记忆写的)。

问题是,order_by指的是节点模型。

但是,有一种方法可以

Node.objects.get(name='x').edges.extra(select={'weight':'%s.weight' % Edge._meta.db_table}).order_by('weight')

这将迫使 ORM:

  1. 添加“重量”字段,通常会被省略。
  2. 按它排序结果。

查询的数量应该和prefetch_query工作的一样,一个获取节点,第二个获取相关节点。

不幸的是,这不是一个非常“干净”的解决方案,因为我们需要使用_meta.

于 2013-02-16T16:16:45.713 回答
0

虽然不是很干净。。

//Untested Code
Node n = Node.objects.get(name="x")

//This would return To Node IDs' ordered by weight

n.edges.filter(from_node = n).values_list('to_node', flat=True).order_by('weight')
于 2013-02-16T01:45:14.143 回答