2010-04-05 11 views
12

Sono sorpreso che questa domanda non sia venuta fuori. Non ho trovato molto sul web.equivalente Django per l'ultimo inserimento per ogni utente

Utilizzo di Entry.objects.latest('created_at') È possibile recuperare l'ultima voce per tutti gli oggetti di immissione, ma dire se si desidera l'ultima voce per ciascun utente? Questo è qualcosa di simile a una query del record SQL più recente. Ma come ottengo ciò usando l'ORM? Ecco il mio approccio Mi chiedo se è il modo più efficiente per fare ciò che voglio.

Prima eseguo una query secondaria: gli oggetti sono raggruppati per utente e il campo Max (l'ultimo) created_by viene restituito per ogni utente (created_at__max) Quindi filtro gli oggetti Entry in base ai risultati nella sottoquery e ottengo gli oggetti richiesti.

Entry.objects.filter(created_at__in=Entry.objects.values('user').annotate(Max('created_at')).values_list('created_at__max')) 

o utilizzando un manager:

class UsersLatest(models.Manager): 

    def get_query_set(self): 
     return super(UsersLatest,self).get_query_set().filter(created_at__in=self.model.objects.values('user').annotate(Max('created_at')).values_list('created_at__max')) 

C'è un modo più efficiente? possibilmente senza sub-query?

Grazie,

Paul

+1

Mi stavo giocando con l'idea di aggiungere un is_latest booleano al mio modello, che ho impostato quando ho salvato una nuova voce nel mio metodo form.save, quindi ho recuperato il precedente oggetto is_latest e lo ho reso falso --- quindi sono due passaggi per ogni voce salvata. Non so se questo è il modo migliore per andare. Eventuali suggerimenti? – Paul

risposta

0

Non riesco a pensare una singola query SQL prime che scaricherà il set è necessario, quindi dubito che sia possibile costruire un QuerySet con questi risultati.

1

Il progetto di QuerySet dipende da cosa si intende utilizzare per. Non sono sicuro del motivo per cui si sta uscendo dall'iteratore QuerySet con il metodo values_list alla fine. Immagino che tu abbia una lista di stati di utenti in cui mostri l'ultima attività in base a quel modello di voci. Per questo si consiglia di provare questo:

Users.objects.all().annotate(latest_activity=Max('entries__created_at')) 

E poi un ciclo tra gli utenti facilmente nel modello con

{% for user in users %} 
{{ user.full_name }} 
{{ user.latest_activity|date: "m/d/Y" }} 
{% endfor %} 
+0

Ciao grazie per il tuo suggerimento, buon approccio. annotate (Max ('created_at')) restituisce un elenco di dizionari contenenti dati 'user' e 'created_at__max', utilizzo values_list ('created_at__max') per filtrare i dati in un elenco da utilizzare con created_at__in = – Paul

1

Lo SQL prime sarebbe

SELECT entry.id, entry.title, entry.content, entry.user_id, entry.created_at 
FROM 
    entry 
WHERE 
    entry.created_at = (SELECT Max(e2.created_at) from entry as e2 where e2.user_id = entry.user_id) 

Quindi una possibilità è utilizzando l'argomento where di extra() modifier:

Entry.objects.extra(where='entry.created_at = (SELECT Max(e2.created_at) from entry as e2 where e2.user_id = entry.user_id)') 

Ovviamente, probabilmente dovresti cambiare entry in qualunque sia il nome effettivo della tabella nel database. Supponendo che sei a tuo agio guardando ._meta, si può provare questo:

Entry.objects.extra(where= 
    '%(table)s.created_at = (SELECT Max(e2.created_at) from %(table)s as e2 where e2.user_id = %(table)s.user_id)' % { 'table':Entry._meta.db_table } 
) 

C'è probabilmente un più elegante modo per ottenere il nome di una tabella.

+0

Grazie per il suggerimento , fa esattamente ciò di cui ho bisogno. Ho controllato la sql della mia query Django usando ._as_sql() ed è quasi esattamente la stessa – Paul

0

Ho avuto un problema simile e lo ha fatto in questo modo:

priorities = obj.books_set.values('category').annotate(priority=Max('priority')) 

Nota: I annotare la priorità massima come priorità, perché sarò riutilizzare l'output come condizione di filtro.

È un elenco di categorie con priorità min.Poi faccio questo:

>>> priorities[0] 
{'category': 1, 'priority': 10} 

Voglio trovare libri che hanno categoria & coppia priorità tra uno nella lista. Alla fine la condizione di set di query dovrebbe essere simile a questo:

Q(priorities[0]) | Q(priorities[1]) | ... 

di farlo in una sola riga, utilizzare reduce su Q.__or__:

reduce(Q.__or__, (Q(**x) for x in priorities)) 

Lo so che è un po 'peggio di SQL prime, ma più sicuro. Commenta questo codice se lo usi, perché è difficile da leggere.

3

Utilizzando order_by e distinct:

Entry.objects.all().order_by('user', 'created_at').distinct('user') 

Poi per le prestazioni l'aggiunta dell'indice insieme su 'utente' e 'i campi created_at.

Ma penso che il modo di produzione reale sia quello di utilizzare Redis per memorizzare nella cache e aggiornare l'elenco di un id delle ultime voci di utenti.

Problemi correlati