Como converter um Django QuerySet em uma lista


121

Eu tenho o seguinte:

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

então mais tarde:

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)

Recebo um erro:

'QuerySet' object has no attribute 'remove'

Tentei todos os tipos para converter o QuerySet em um conjunto ou lista padrão. Nada funciona.

Como posso remover um item do QuerySet para que ele não o exclua do banco de dados e não retorne um novo QuerySet (já que está em um loop que não funciona)?

Respostas:


42

Você poderia fazer isso:

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)

Leia quando QuerySets são avaliados e observe que não é bom carregar o resultado inteiro na memória (por exemplo, via list()).

Referência: itertools.ifilter

Atualização em relação ao comentário:

Existem várias maneiras de fazer isso. Um (que provavelmente não é o melhor em termos de memória e tempo) é fazer exatamente o mesmo:

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)

Eu adicionei mais uma linha ao meu exemplo de código acima, excluindo a mesma entrada de exising_question_answers. é possível usar um ifilter para isso também de alguma forma?
— john

Vou marcar isso como correto porque eu não sabia sobre filtro e tinha me esquecido dos lambdas.
— john

315

Por que não ligar list()para o Queryset?

answers_list = list(answers)

Isso também avaliará QuerySet/ execute a consulta. Você pode então remover / adicionar dessa lista.


9
Cuidado com isso. Ao converter isso para uma lista, o sinalizador distinto pode ser desconsiderado.
— rh0dium

Eu posso fazer isso de forma independente, mas posso fazer em queryset na forma de django. Alguma ideia do porquê?
— ismailsunni

Em caso afirmativo, lance para sete depois volte para listpara obter os únicos.
— radtek

36

É um pouco difícil seguir o que você realmente está tentando fazer. Sua primeira instrução parece que você está buscando os mesmos objetos QuerySet of Answer duas vezes. Primeiro via answer_set.answers.all()e depois novamente via .filter(id__in=...). Verifique novamente o shell e veja se isso lhe dará a lista de respostas que você está procurando:

answers = answer_set.answers.all()

Depois de limpar isso para que seja um pouco mais fácil para você (e outros que estão trabalhando no código) ler, você pode dar uma olhada em .exclude () e no campo __in .

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

new_answers = answers.exclude(question_answer__in=existing_question_answers)

A pesquisa acima pode não sincronizar com as definições do seu modelo, mas provavelmente o deixará perto o suficiente para terminar o trabalho sozinho.

Se você ainda precisa obter uma lista de valores de id, então você deseja brincar com .values_list () . No seu caso, você provavelmente desejará adicionar o opcional flat = True.

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

Obrigado pela sua resposta. Infelizmente, não forneci detalhes suficientes para mostrar que não posso usar sua abordagem.
— john

1
Melhor solução para o problema descrito. Quero adicionar new_answers = answers.exclude(question_answer__in=existing_question_answers.values_list('id', flat=True))@istruble
— aquaman

A maneira mais limpa é flat=TrueObrigado !!!!!!!
— Cho

18

Pelo uso do operador slice com o parâmetro step que causaria avaliação do queryset e criaria uma lista.

list_of_answers = answers[::1]

ou inicialmente você poderia ter feito:

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

Até onde eu sei, o django queryset não suporta indexação negativa.
— Alexey Sidash

Ok Alexey. Você está bem aqui. Eu atualizei a resposta.
— Ankit Singh

15

Você pode converter diretamente usando a listpalavra - chave. Por exemplo:

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

Usando o código acima, você pode converter diretamente um resultado de conjunto de consulta em um list.

Aqui listestá a palavra-chave e objé o resultado do conjunto de consultas e list1é variável nessa variável em que estamos armazenando o resultado convertido em list.


1
Mas se você fizer isso, não funcionará: o list1 = list(emp.objects.all())que parece contra-intuitivo.
— geoidésico de

4

Por que não ligar .values('reqColumn1','reqColumn2')ou .values_list('reqColumn1','reqColumn2')no queryset?

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

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

OU

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

result = [(value1,value2)]

Você pode fazer todas as operações neste QuerySet, que você faz para a 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 converte django queryset em lista python


0

Experimente isso 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)

Irá devolver-lhe uma lista com valores de coluna especificados


0

em vez de remove()você pode usar a exclude()função para remover um objeto do queryset. sua sintaxe é semelhante afilter()

por exemplo : -

qs = qs.exclude(id= 1)

no código acima, ele remove todos os objetos de qs com id '1'

informações adicionais : -

filter()usado para selecionar objetos específicos, mas exclude()usado para remover

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.