Як перетворити запит набору Джанго в список


121

У мене є таке:

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

потім пізніше:

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)

Я отримую помилку:

'QuerySet' object has no attribute 'remove'

Я намагався всіляко перетворити QuerySet у стандартний набір або список. Нічого не працює.

Як я можу видалити елемент із QuerySet, щоб він не видалив його з бази даних та не повернув новий QuerySet (оскільки він знаходиться в циклі, який не працюватиме)?

Відповіді:


42

Ви можете це зробити:

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)

Прочитайте, коли оцінюються QuerySets, і зауважте, що не добре завантажувати весь результат у пам'ять (наприклад, через list()).

Довідка: itertools.ifilter

Оновлення щодо коментаря:

Існують різні способи зробити це. Одне (що, мабуть, не найкраще з точки зору пам’яті та часу) - це зробити саме те саме:

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)

Я додав ще один рядок до мого зразка коду вище, видаляючи той самий запис із exising_question_answers. чи можна використовувати ifilter для цього теж якось?
Джон

Я відзначу це правильним, бо не знав про фільтр і забув про лямбда.
Джон

315

Чому б просто не подзвонити list()на Queryset?

answers_list = list(answers)

Це також оцінить QuerySet/ запустити запит. Потім ви можете видалити / додати зі цього списку.


9
Будьте обережні з цим. Якщо ви подаєте це до списку aa, то окремий прапор може не враховуватися.
rh0dium

Я можу це зробити самостійно, але можу це зробити в наборі запитів у формі джанго. Будь-яка ідея чому?
ismailsunni

Якщо так, то киньте на, setа потім назад, listщоб отримати унікальні.
radtek

36

Трохи важко слідкувати за тим, що ти насправді намагаєшся зробити. Ваше перше твердження виглядає так, що ви можете двічі отримувати один і той самий точний QuerySet of Answer. Спочатку через, answer_set.answers.all()а потім знову через .filter(id__in=...). Перевірте оболонку та перевірте, чи це дасть вам список відповідей, які ви шукаєте:

answers = answer_set.answers.all()

Після того, як ви почистили це, то вам (і іншим, хто працює над кодом) трохи простіше прочитати, ви можете заглянути в .exclude () та пошук у __in полі .

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

new_answers = answers.exclude(question_answer__in=existing_question_answers)

Вищеописаний пошук може не синхронізуватися з визначеннями вашої моделі, але, ймовірно, ви отримаєте достатньо близько, щоб самостійно закінчити роботу.

Якщо вам все-таки потрібно отримати список значень id, тоді ви хочете пограти з .values_list () . У вашому випадку ви, ймовірно, захочете додати необов’язковий плоский = True.

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

Дякую за вашу відповідь. На жаль, я не представив достатньо деталей, щоб показати, що не можу використовувати ваш підхід.
Джон

1
Найкраще рішення описаної проблеми. Я хочу додати new_answers = answers.exclude(question_answer__in=existing_question_answers.values_list('id', flat=True))@istruble
aquaman

Найчистіший спосіб - flat=TrueДякую !!!!!!!
Чо

18

За допомогою оператора зрізів з параметром кроку, який би викликав оцінку набору запитів та створив список.

list_of_answers = answers[::1]

або спочатку ви могли зробити:

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

Наскільки мені відомо, набір запитів django не підтримує негативну індексацію.
Олексій Сидаш

Гаразд, Олексій. Ви тут. Я оновив відповідь.
Анкіт Сінгх

15

Ви можете безпосередньо конвертувати за допомогою listключового слова. Наприклад:

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

Використовуючи наведений вище код, ви можете безпосередньо перетворити результат набору запитів у list.

Ось listце ключове слово і objє результатом набору запитів і list1є змінною в цій змінній, в якій ми зберігаємо перетворений результат, який в list.


1
Але якщо це зробити, це не спрацює: list1 = list(emp.objects.all())це здається контрінтуїтивним.
геодезик

4

Чому б не просто зателефонувати .values('reqColumn1','reqColumn2')або .values_list('reqColumn1','reqColumn2')набір запитів?

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

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

АБО

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

result = [(value1,value2)]

Ви можете виконати всі операції на цьому QuerySet, які ви робите для списку.


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)

цей код перетворить набір запитів django в список python


0

Спробуйте це 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)

Він поверне вам список із заданими значеннями стовпців


0

замість remove()вас можна використовувати exclude()функцію для видалення об'єкта з набору запитів. його синтаксис схожий наfilter()

наприклад : -

qs = qs.exclude(id= 1)

у наведеному вище коді він видаляє всі об’єкти з qs з id '1'

додаткова інформація : -

filter()використовується для вибору конкретних об'єктів, але exclude()використовується для видалення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.