217

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)?

Piper
  • 1,266
  • 3
  • 15
  • 26
john
  • 2,199
  • 2
  • 15
  • 9

10 Answers10

480

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.

phoenix
  • 7,988
  • 6
  • 39
  • 45
Zeppomedio
  • 5,911
  • 3
  • 18
  • 18
  • 16
    Be careful with this. When you cast this to a a list the distinct flag may become disregarded. – rh0dium Oct 01 '15 at 19:52
  • I can do it independently, but I can do it in queryset in django form. Any idea why? – ismailsunni Oct 29 '15 at 14:16
  • 3
    If so, then cast to `set` and then back to `list` to get the unique ones. – radtek Sep 06 '19 at 15:38
  • @rh0dium - do you have any reference for this? I'm using this in my code and I'm finding it hard to understand how it might affect uniqueness (not saying it won't - just that I can't connect the dots). – zerohedge Sep 12 '20 at 23:39
66

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)
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • i've added one more line to my code sample above, deleting the same entry from exising_question_answers. is it possible to use a ifilter for that too somehow? – john Dec 12 '10 at 22:43
  • I'll mark this as correct because I didn't know about filter and had forgotten about lambdas. – john Dec 13 '10 at 20:10
56

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)
istruble
  • 13,363
  • 2
  • 47
  • 52
  • Thanks for your answer. Unfortunately I didn't provide enough detail to show that I can't use your approach. – john Dec 13 '10 at 20:11
  • 3
    Best solution for described problem. I want add `new_answers = answers.exclude(question_answer__in=existing_question_answers.values_list('id', flat=True))` @istruble – aquaman Apr 28 '16 at 13:45
29

By the use of slice operator with step parameter which would cause evaluation of the queryset and create a list.

list_of_answers = answers[::1]

or initially you could have done:

answers = Answer.objects.filter(id__in=[answer.id for answer in
        answer_set.answers.all()])[::1]
Ankit Singh
  • 594
  • 4
  • 16
19

You can directly convert using the list keyword. For example:

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

Using the above code you can directly convert a query set result into a list.

Here list is keyword and obj is result of query set and list1 is variable in that variable we are storing the converted result which in list.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
patel shahrukh
  • 556
  • 5
  • 9
10

Try this 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)

It will return you a list with specified column values

Sakeer
  • 1,885
  • 3
  • 24
  • 43
  • 3
    Careful, I believe this still returns a Queryset. [From docs](https://docs.djangoproject.com/en/3.2/ref/models/querysets/#django.db.models.query.QuerySet.values_list) – SuaveSouris Aug 23 '22 at 18:40
  • It still returns QuerySet but combining from the answers above. answers = list(Answer.objects.filter(id__in=[answer.id for answer in answer_set.answers.all()]).values_list('column_name', flat=True)). Add the list() to convert the QuerySet to list. [Note]: This is applicable only to data that do not want a key for a result in query like me with a specific values that is needed like ID. – Bryan Lim Dec 05 '22 at 14:46
9

Use python list() function

list(). Force evaluation of a QuerySet by calling list() on it. For example:

answers = list(answer_set.answers.all())
Emmanuel Mtali
  • 4,383
  • 3
  • 27
  • 53
Hamed Bakhti
  • 101
  • 2
  • 6
  • Please provide additional details in your answer. As it's currently written, it's hard to understand your solution. – Community Sep 04 '21 at 12:12
7

Why not just call .values('reqColumn1','reqColumn2') or .values_list('reqColumn1','reqColumn2') on the queryset?

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

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

OR

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

result = [(value1,value2)]

You can able to do all the operation on this QuerySet, which you do for list .

Piyush S. Wanare
  • 4,703
  • 6
  • 37
  • 54
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)

this code convert django queryset to python list

0

instead of remove() you can use exclude() function to remove an object from the queryset. it's syntax is similar to filter()

eg : -

qs = qs.exclude(id= 1)

in above code it removes all objects from qs with id '1'

additional info :-

filter() used to select specific objects but exclude() used to remove