Come convertire un Django QuerySet in un elenco


121

Ho il seguente:

answers = Answer.objects.filter(id__in=[answer.id for answer in answer_set.answers.all()])

poi più tardi:

for i in range(len(answers)):
    # iterate through all existing QuestionAnswer objects
    for existing_question_answer in existing_question_answers:
        # if an answer is already associated, remove it from the
        # list of answers to save
        if answers[i].id == existing_question_answer.answer.id:
            answers.remove(answers[i])           # doesn't work
            existing_question_answers.remove(existing_question_answer)

Ottengo un errore:

'QuerySet' object has no attribute 'remove'

Ho provato tutti i tipi per convertire il QuerySet in un set o un elenco standard. Niente funziona.

Come posso rimuovere un elemento dal QuerySet in modo che non lo elimini dal database e non restituisca un nuovo QuerySet (poiché è in un ciclo che non funzionerà)?

Risposte:


42

Potresti farlo:

import itertools

ids = set(existing_answer.answer.id for existing_answer in existing_question_answers)
answers = itertools.ifilter(lambda x: x.id not in ids, answers)

Leggere quando vengono valutati i QuerySet e notare che non è bene caricare l'intero risultato in memoria (ad esempio tramite list()).

Riferimento: itertools.ifilter

Aggiornamento in merito al commento:

Ci sono vari modi per farlo. Uno (che probabilmente non è il migliore in termini di memoria e tempo) è fare esattamente lo stesso:

answer_ids = set(answer.id for answer in answers)
existing_question_answers = filter(lambda x: x.answer.id not in answers_id, existing_question_answers)

Ho aggiunto un'altra riga al mio esempio di codice sopra, eliminando la stessa voce da exising_question_answers. è possibile utilizzare un ifilter anche per questo in qualche modo?
giovanni

Lo contrassegno come corretto perché non conoscevo il filtro e mi ero dimenticato dei lambda.
giovanni

315

Perché non chiamare list()il Queryset?

answers_list = list(answers)

Questo valuterà anche il QuerySet/ eseguirà la query. È quindi possibile rimuovere / aggiungere da tale elenco.


9
Stai attento con questo. Quando lanci questo a una lista, la bandiera distinta potrebbe essere ignorata.
rh0dium

Posso farlo in modo indipendente, ma posso farlo in queryset in forma django. Qualche idea sul perché?
ismailsunni

In tal caso, trasmetti a sete poi torna a listper ottenere quelli unici.
radtek

36

È un po 'difficile seguire quello che stai veramente cercando di fare. La tua prima istruzione sembra che tu stia recuperando due volte gli stessi oggetti QuerySet di risposte esatti. Prima via answer_set.answers.all()e poi di nuovo via .filter(id__in=...). Controlla due volte nella shell e vedi se questo ti darà l'elenco delle risposte che stai cercando:

answers = answer_set.answers.all()

Una volta che hai ripulito ciò, quindi è un po 'più facile per te (e altri che lavorano sul codice) leggere, potresti voler esaminare .exclude () e la ricerca nel campo __in .

existing_question_answers = QuestionAnswer.objects.filter(...)

new_answers = answers.exclude(question_answer__in=existing_question_answers)

La ricerca sopra potrebbe non sincronizzarsi con le definizioni del tuo modello, ma probabilmente ti avvicinerà abbastanza da finire il lavoro da solo.

Se hai ancora bisogno di ottenere un elenco di valori id, allora vuoi giocare con .values_list () . Nel tuo caso probabilmente vorrai aggiungere il flat opzionale = True.

answers.values_list('id', flat=True)

Grazie per la tua risposta. Sfortunatamente non ho fornito dettagli sufficienti per dimostrare che non posso utilizzare il tuo approccio.
giovanni

1
La migliore soluzione per il problema descritto. Voglio aggiungere new_answers = answers.exclude(question_answer__in=existing_question_answers.values_list('id', flat=True))@istruble
aquaman

Il modo più pulito è flat=Truegrazie !!!!!!!
Cho

18

Mediante l'uso dell'operatore slice con parametro step che provocherebbe la valutazione del set di query e creare un elenco.

list_of_answers = answers[::1]

oppure inizialmente avresti potuto fare:

answers = Answer.objects.filter(id__in=[answer.id for answer in
        answer_set.answers.all()])[::1]

Per quanto ne so, django queryset non supporta l'indicizzazione negativa.
Alexey Sidash

Va bene Alexey. Sei proprio qui. Ho aggiornato la risposta.
Ankit Singh

15

Puoi convertire direttamente usando la listparola chiave. Per esempio:

obj=emp.objects.all()
list1=list(obj)

Utilizzando il codice precedente è possibile convertire direttamente il risultato di un set di query in un file list.

Ecco la listparola chiave ed objè il risultato del set di query ed list1è variabile in quella variabile in cui stiamo memorizzando il risultato convertito che in list.


1
Ma se lo fai non funziona: il list1 = list(emp.objects.all())che sembra controintuitivo.
Geoidesic

4

Perché non chiamare .values('reqColumn1','reqColumn2')o semplicemente .values_list('reqColumn1','reqColumn2')il queryset?

answers_list = models.objects.values('reqColumn1','reqColumn2')

result = [{'reqColumn1':value1,'reqColumn2':value2}]

O

answers_list = models.objects.values_list('reqColumn1','reqColumn2')

result = [(value1,value2)]

Puoi eseguire tutte le operazioni su questo QuerySet, che fai per list.


1
def querySet_to_list(qs):
    """
    this will return python list<dict>
    """
    return [dict(q) for q in qs]

def get_answer_by_something(request):
    ss = Answer.objects.filter(something).values()
    querySet_to_list(ss) # python list return.(json-able)

questo codice converte il set di query django in un elenco di python


0

Prova questo values_list('column_name', flat=True).

answers = Answer.objects.filter(id__in=[answer.id for answer in answer_set.answers.all()]).values_list('column_name', flat=True)

Ti restituirà un elenco con i valori di colonna specificati


0

invece di remove()utilizzare la exclude()funzione per rimuovere un oggetto dal set di query. la sua sintassi è simile afilter()

ad esempio : -

qs = qs.exclude(id= 1)

nel codice sopra rimuove tutti gli oggetti da qs con id '1'

informazioni aggiuntive : -

filter()utilizzato per selezionare oggetti specifici ma exclude()utilizzato per rimuovere

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.