Django: результаты, связанные с prefetch, упорядоченные полем промежуточной таблицы

как я могу prefetch_related объекты в 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 не имеет никакого эффекта.

Edit:

мой лучший ответ до сих пор

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

3 ответов


просто концептуальная идея (написано по памяти).

проблема в том, что order_by относится к модели узла.

, есть способ
Node.objects.get(name='x').edges.extra(select={'weight':'%s.weight' % Edge._meta.db_table}).order_by('weight')

это заставит ОРМ:

  1. добавить поле "вес", которое обычно опускается.
  2. заказать результаты по нему.

количество запросов должно быть таким же, как если prefetch_query работали, один узел, второй, чтобы получить соответствующие узлы.

к сожалению, это не очень "чистое" решение, как нам нужно использовать _meta.


В настоящее время вы также можете использовать класс 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

не так чисто, хотя..

//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')