How to convert a Django QuerySet to a list?

I have the following:

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

then later:

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)

I get an error:

'QuerySet' object has no attribute 'remove'

I've tried all sorts to convert the QuerySet to a standard set or list. Nothing works.

How can I remove an item from the QuerySet so it doesn't delete it from the database, and doesn't return a new QuerySet (since it's in a loop that won't work)?


Solution 1:

Why not just call list() on the Queryset?

answers_list = list(answers)

This will also evaluate the QuerySet/run the query. You can then remove/add from that list.

Solution 2:

You could do this:

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)

Read when QuerySets are evaluated and note that it is not good to load the whole result into memory (e.g. via list()).

Reference: itertools.ifilter

Update with regard to the comment:

There are various ways to do this. One (which is probably not the best one in terms of memory and time) is to do exactly the same :

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)

Solution 3:

It is a little hard to follow what you are really trying to do. Your first statement looks like you may be fetching the same exact QuerySet of Answer objects twice. First via answer_set.answers.all() and then again via .filter(id__in=...). Double check in the shell and see if this will give you the list of answers you are looking for:

answers = answer_set.answers.all()

Once you have that cleaned up so it is a little easier for you (and others working on the code) to read you might want to look into .exclude() and the __in field lookup.

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

new_answers = answers.exclude(question_answer__in=existing_question_answers)

The above lookup might not sync up with your model definitions but it will probably get you close enough to finish the job yourself.

If you still need to get a list of id values then you want to play with .values_list(). In your case you will probably want to add the optional flat=True.

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