2013-02-15 6 views
7

Come posso prefetch_related oggetti in Django e ordinarli da un campo in una tabella intermediario?Django: prefetch_related risultati ordinati da un campo di un tavolo intermedio

Ecco i modelli con cui sto lavorando:

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) 

Dato un nodo, mi piacerebbe precaricare tutti i nodi connessi, in ordine di peso.

Quando uso questa query:

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

l'order_by non ha alcun effetto.

Edit:

La mia migliore risposta finora

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

Allora, invece di iterazione n.edges (come preferirei), ho itera edges.to_node

+0

Si sta posizionando l'ordine per clausola sulla tabella dei nodi non sulla tabella dei bordi. Quindi, il risultato menzionato è previsto. –

+0

Infatti, ma come può essere messo sul tavolo di bordo? –

risposta

4

Solo un'idea concettuale (scritta dalla memoria).

Il problema è che il order_by si riferisce al modello di nodo.

Tuttavia, v'è un modo per

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

Questo forzerà l'ORM a:

  1. Registrare campo 'peso', che normalmente sarebbe omesso.
  2. Ordinare i risultati.

Il numero di query deve essere lo stesso di se lo prefetch_query ha funzionato, uno per ottenere il nodo, in secondo luogo per ottenere i relativi nodi.

Purtroppo questa non è una soluzione molto "pulita", poiché è necessario utilizzare _meta.

+1

Potrebbe non essere pulito ma posso confermare che funziona. Grazie! –

0
Non

che pulito anche se ..

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

Al giorno d'oggi, è anche possibile utilizzare la classe Prefetch per raggiungere questo obiettivo:

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

Oppure, se si vuole fare questo tutto il tempo come un default, si può guardare in meta ordinamento sulla tabella intermedia, qualcosa del tipo:

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