Jak przekonwertować zestaw Django QuerySet na listę


121

Mam:

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

potem później:

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)

Pojawia się błąd:

'QuerySet' object has no attribute 'remove'

Próbowałem przerobić QuerySet na standardowy zestaw lub listę. Nic nie działa.

Jak mogę usunąć element z QuerySet, aby nie usuwał go z bazy danych i nie zwracał nowego zestawu QuerySet (ponieważ jest w pętli, która nie działa)?

Odpowiedzi:


42

Możesz to zrobić:

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)

Przeczytaj, kiedy QuerySets są oceniane i zauważ, że nie jest dobrze ładować cały wynik do pamięci (np. Via list()).

Odniesienie: itertools.ifilter

Aktualizacja dotycząca komentarza:

Można to zrobić na różne sposoby. Jednym (który prawdopodobnie nie jest najlepszy pod względem pamięci i czasu) jest zrobienie dokładnie tego samego:

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)

dodałem jeszcze jeden wiersz do powyższego przykładu kodu, usuwając ten sam wpis z exising_question_answers. czy można w jakiś sposób użyć do tego filtra ifilter?
Jan

Oznaczę to jako poprawne, ponieważ nie wiedziałem o filtrze i zapomniałem o lambdach.
john

315

Dlaczego nie po prostu zadzwoń list()po Queryset?

answers_list = list(answers)

Spowoduje to również ocenę QuerySet/ uruchom zapytanie. Następnie możesz usunąć / dodać z tej listy.


9
Ostrożnie z tym. Kiedy rzucasz to na listę, odrębna flaga może zostać zignorowana.
rh0dium

Mogę to zrobić niezależnie, ale mogę to zrobić w queryset w postaci django. Każdy pomysł, dlaczego?
ismailsunni

Jeśli tak, rzuć do, seta potem z powrotem, listaby uzyskać unikalne.
radtek

36

Trochę trudno jest śledzić to, co naprawdę próbujesz zrobić. Twoje pierwsze stwierdzenie wygląda na to, że możesz dwukrotnie pobrać dokładnie te same obiekty QuerySet of Answer. Najpierw przez, answer_set.answers.all()a potem ponownie przez .filter(id__in=...). Dokładnie sprawdź w powłoce i zobacz, czy da ci to listę odpowiedzi, których szukasz:

answers = answer_set.answers.all()

Po wyczyszczeniu tego, aby czytanie było trochę łatwiejsze dla Ciebie (i innych osób pracujących nad kodem), możesz zajrzeć do .exclude () i wyszukiwania pól __in .

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

new_answers = answers.exclude(question_answer__in=existing_question_answers)

Powyższe wyszukiwanie może nie zsynchronizować się z definicjami modelu, ale prawdopodobnie zbliży Cię na tyle, aby samemu zakończyć pracę.

Jeśli nadal potrzebujesz listy wartości identyfikatorów, powinieneś pobawić się z .values_list () . W twoim przypadku prawdopodobnie będziesz chciał dodać opcjonalny flat = True.

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

Dziękuję za odpowiedź. Niestety nie podałem wystarczająco szczegółów, aby pokazać, że nie mogę zastosować twojego podejścia.
john

1
Najlepsze rozwiązanie opisanego problemu. Chcę dodać new_answers = answers.exclude(question_answer__in=existing_question_answers.values_list('id', flat=True))@istruble
aquaman

Najczystszym sposobem jest flat=Truedzięki !!!!!!!
Cho

18

Poprzez użycie operatora wycinka z parametrem step, który spowodowałby ocenę zestawu zapytań i utworzenie listy.

list_of_answers = answers[::1]

lub początkowo mogłeś zrobić:

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

O ile wiem, django queryset nie obsługuje indeksowania ujemnego.
Alexey Sidash

Ok, Alexey. Jesteś tutaj. Zaktualizowałem odpowiedź.
Ankit Singh

16

Możesz bezpośrednio konwertować za pomocą listsłowa kluczowego. Na przykład:

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

Używając powyższego kodu możesz bezpośrednio przekonwertować wynik zestawu zapytań na plik list.

Oto listsłowo kluczowe i objjest wynikiem zestawu zapytań i list1jest zmienną w tej zmiennej, w której przechowujemy przekonwertowany wynik, w którym list.


1
Ale jeśli to zrobisz, nie zadziała: list1 = list(emp.objects.all())co wydaje się sprzeczne z intuicją.
geoidesic

4

Dlaczego po prostu nie zadzwonić .values('reqColumn1','reqColumn2')lub nie .values_list('reqColumn1','reqColumn2')włączyć zestawu zapytań?

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

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

LUB

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

result = [(value1,value2)]

Możesz wykonać wszystkie operacje na tym QuerySet, które wykonujesz dla 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)

ten kod konwertuje zestaw zapytań django na listę Pythona


0

Spróbuj tego 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)

Zwróci listę z określonymi wartościami kolumn


0

zamiast remove()ciebie możesz użyć exclude()funkcji do usunięcia obiektu z zestawu zapytań. jego składnia jest podobna dofilter()

np . : -

qs = qs.exclude(id= 1)

w powyższym kodzie usuwa wszystkie obiekty z qs o id '1'

dodatkowe informacje : -

filter()służy do wybierania określonych obiektów, ale exclude()służy do usuwania

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.