Django: результаты 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 не имеет никакого эффекта.

Изменить:

Мой лучший ответ на данный момент

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


person Ollie Glass    schedule 15.02.2013    source источник
comment
Вы размещаете предложение order by в таблице узлов, а не в таблице краев. Так что указанный результат ожидаем.   -  person Raunak Agarwal    schedule 16.02.2013
comment
Действительно, но как его поставить на край стола?   -  person Ollie Glass    schedule 16.02.2013


Ответы (3)


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

Проблема в том, что order_by относится к модели Node.

Однако есть способ

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

Это заставит ORM:

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

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

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

person Tomek Kopczuk    schedule 16.02.2013

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

Хотя не так чисто..

//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')
person Raunak Agarwal    schedule 16.02.2013