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.