Cómo convertir un QuerySet de Django en una lista


121

Tengo lo siguiente:

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

Entonces despúes:

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)

Me sale un error:

'QuerySet' object has no attribute 'remove'

He intentado todo tipo de convertir el QuerySet en un conjunto o lista estándar. Nada funciona.

¿Cómo puedo eliminar un elemento del QuerySet para que no lo elimine de la base de datos y no devuelva un nuevo QuerySet (ya que está en un bucle que no funcionará)?

Respuestas:


42

Podrías hacer esto:

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)

Lea cuando se evalúan los QuerySets y tenga en cuenta que no es bueno cargar el resultado completo en la memoria (por ejemplo, a través de list()).

Referencia: itertools.ifilter

Actualización con respecto al comentario:

Hay varias maneras de hacer esto. Una (que probablemente no sea la mejor en términos de memoria y tiempo) es hacer exactamente lo mismo:

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)

He agregado una línea más a mi ejemplo de código anterior, eliminando la misma entrada de exising_question_answers. ¿Es posible usar un ifilter para eso también de alguna manera?
juan

Marcaré esto como correcto porque no sabía sobre el filtro y me había olvidado de las lambdas.
Juan

315

¿Por qué no llamar list()al Queryset?

answers_list = list(answers)

Esto también evaluará QuerySet/ ejecutará la consulta. A continuación, puede eliminar / agregar de esa lista.


9
Tenga cuidado con esto. Cuando echas esto a una lista, la bandera distinta puede ignorarse.
rh0dium

Puedo hacerlo de forma independiente, pero puedo hacerlo en queryset en forma de django. ¿Alguna idea de por qué?
ismailsunni

Si es así, lanza ay setluego vuelve a listpara obtener los únicos.
radtek

36

Es un poco difícil seguir lo que realmente estás tratando de hacer. Parece que su primera declaración puede estar obteniendo exactamente el mismo objeto QuerySet of Answer dos veces. Primero vía answer_set.answers.all()y luego de nuevo vía .filter(id__in=...). Verifique dos veces el shell y vea si esto le dará la lista de respuestas que está buscando:

answers = answer_set.answers.all()

Una vez que lo hayas limpiado para que sea un poco más fácil para ti (y para otras personas que trabajan en el código) leerlo, es posible que desees examinar .exclude () y la búsqueda de campo __in .

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

new_answers = answers.exclude(question_answer__in=existing_question_answers)

Es posible que la búsqueda anterior no se sincronice con las definiciones de su modelo, pero probablemente lo acercará lo suficiente como para terminar el trabajo usted mismo.

Si aún necesita obtener una lista de valores de identificación, entonces quiere jugar con .values_list () . En su caso, probablemente desee agregar el opcional flat = True.

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

Gracias por tu respuesta. Lamentablemente, no proporcioné suficientes detalles para demostrar que no puedo usar su enfoque.
Juan

1
La mejor solución para el problema descrito. Quiero agregar new_answers = answers.exclude(question_answer__in=existing_question_answers.values_list('id', flat=True))@istruble
aquaman

La forma más limpia es flat=TrueGracias !!!!!!!
Cho

18

Mediante el uso de un operador de corte con un parámetro de paso que provocaría la evaluación del conjunto de consultas y crearía una lista.

list_of_answers = answers[::1]

o inicialmente podrías haber hecho:

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

Hasta donde yo sé, django queryset no admite la indexación negativa.
Alexey Sidash

Está bien Alexey. Estás aquí. He actualizado la respuesta.
Ankit Singh

15

Puede convertir directamente usando la listpalabra clave. Por ejemplo:

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

Con el código anterior, puede convertir directamente el resultado de un conjunto de consultas en un archivo list.

Aquí listestá la palabra clave y objes el resultado del conjunto de consultas y list1es variable en esa variable estamos almacenando el resultado convertido en list.


1
Pero si haces esto, no funciona: lo list1 = list(emp.objects.all())que parece contrario a la intuición.
geoidesic

4

¿Por qué no simplemente llamar .values('reqColumn1','reqColumn2')o .values_list('reqColumn1','reqColumn2')en el conjunto de consultas?

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

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

O

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

result = [(value1,value2)]

Puede hacer todas las operaciones en este QuerySet, lo que hace para la lista.


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)

este código convierte el conjunto de consultas de django a la lista de python


0

Prueba esto 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)

Le devolverá una lista con valores de columna especificados


0

en lugar de remove()puede utilizar la exclude()función para eliminar un objeto del conjunto de consultas. su sintaxis es similar afilter()

por ejemplo : -

qs = qs.exclude(id= 1)

en el código anterior, elimina todos los objetos de qs con id '1'

información adicional : -

filter()se utiliza para seleccionar objetos específicos pero se exclude()utiliza para eliminar

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.