Comment convertir un Django QuerySet en liste

121

J'ai ce qui suit:

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

puis plus tard:

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)

J'obtiens une erreur:

'QuerySet' object has no attribute 'remove'

J'ai essayé toutes sortes de convertir le QuerySet en un ensemble ou une liste standard. Rien ne fonctionne.

Comment puis-je supprimer un élément du QuerySet afin qu'il ne le supprime pas de la base de données et ne renvoie pas un nouveau QuerySet (car il est dans une boucle qui ne fonctionnera pas)?

John
la source

Réponses:

42

Vous pouvez faire ceci:

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)

Lisez quand les QuerySets sont évalués et notez qu'il n'est pas bon de charger tout le résultat en mémoire (par exemple via list()).

Référence: itertools.ifilter

Mise à jour par rapport au commentaire:

Il ya différentes manière de faire ceci. L'une (qui n'est probablement pas la meilleure en termes de mémoire et de temps) est de faire exactement la même chose:

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)
Félix Kling
la source
J'ai ajouté une ligne de plus à mon exemple de code ci-dessus, en supprimant la même entrée de exising_question_answers. est-il possible d'utiliser un filtre pour cela aussi d'une manière ou d'une autre?
john
Je marquerai cela comme correct parce que je ne connaissais pas le filtre et j'avais oublié les lambdas.
john
315

Pourquoi ne pas simplement appeler list()le Queryset?

answers_list = list(answers)

Cela évaluera également QuerySet/ exécutera la requête. Vous pouvez ensuite supprimer / ajouter de cette liste.

Zeppomedio
la source
9
Soyez prudent avec cela. Lorsque vous convertissez ceci en une liste, l'indicateur distinct peut être ignoré.
rh0dium
Je peux le faire indépendamment, mais je peux le faire sous forme de requête sous forme django. Une idée pourquoi?
ismailsunni
Si tel est le cas, lancez setet revenez vers listpour obtenir les éléments uniques.
radtek
36

Il est un peu difficile de suivre ce que vous essayez vraiment de faire. Votre première déclaration semble que vous récupérez peut-être deux fois exactement les mêmes objets QuerySet of Answer. D'abord via answer_set.answers.all(), puis de nouveau via .filter(id__in=...). Vérifiez à nouveau dans le shell et voyez si cela vous donnera la liste des réponses que vous recherchez:

answers = answer_set.answers.all()

Une fois que vous avez nettoyé cela afin qu'il soit un peu plus facile pour vous (et pour les autres personnes travaillant sur le code) de lire, vous voudrez peut-être regarder dans .exclude () et la recherche dans le champ __in .

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

new_answers = answers.exclude(question_answer__in=existing_question_answers)

La recherche ci-dessus peut ne pas se synchroniser avec vos définitions de modèle, mais elle vous rapprochera probablement suffisamment pour terminer le travail vous-même.

Si vous avez encore besoin d'obtenir une liste de valeurs d'identifiant, vous voulez jouer avec .values_list () . Dans votre cas, vous voudrez probablement ajouter l'option flat = True.

answers.values_list('id', flat=True)
istruble
la source
Merci pour votre réponse. Malheureusement, je n'ai pas fourni suffisamment de détails pour montrer que je ne peux pas utiliser votre approche.
john
1
Meilleure solution pour le problème décrit. Je veux ajouter new_answers = answers.exclude(question_answer__in=existing_question_answers.values_list('id', flat=True))@istruble
aquaman
Le moyen le plus propre est flat=TrueMerci !!!!!!!
Cho
18

Par l'utilisation d'un opérateur de tranche avec un paramètre d'étape qui provoquerait l'évaluation de l'ensemble de requêtes et créerait une liste.

list_of_answers = answers[::1]

ou au départ vous auriez pu faire:

answers = Answer.objects.filter(id__in=[answer.id for answer in
        answer_set.answers.all()])[::1]
Ankit Singh
la source
Autant que je sache, django queryset ne prend pas en charge l'indexation négative.
Alexey Sidash
D'accord Alexey. Vous êtes ici. J'ai mis à jour la réponse.
Ankit Singh
15

Vous pouvez convertir directement en utilisant le listmot - clé. Par exemple:

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

En utilisant le code ci-dessus, vous pouvez directement convertir un résultat d'ensemble de requêtes en un fichier list.

Voici le listmot-clé et objest le résultat de l'ensemble de requêtes et list1est variable dans cette variable, nous stockons le résultat converti dans list.

Patel Shahrukh
la source
1
Mais si vous faites cela, cela ne fonctionne pas: list1 = list(emp.objects.all())ce qui semble contre-intuitif.
geoidesic
4

Pourquoi ne pas simplement appeler .values('reqColumn1','reqColumn2')ou .values_list('reqColumn1','reqColumn2')sur le jeu de requêtes?

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

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

OU

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

result = [(value1,value2)]

Vous pouvez effectuer toutes les opérations sur ce QuerySet, ce que vous faites pour la liste.

Piyush S. Wanare
la source
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)

ce code convertit le jeu de requêtes django en liste python

EunSeong Lee
la source
0

Essayez ceci 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)

Il vous renverra une liste avec des valeurs de colonne spécifiées

Sakeer
la source
0

au lieu de remove()vous, vous pouvez utiliser la exclude()fonction pour supprimer un objet du jeu de requêtes. sa syntaxe est similaire àfilter()

par exemple : -

qs = qs.exclude(id= 1)

dans le code ci-dessus, il supprime tous les objets de qs avec l'ID '1'

info supplémentaire : -

filter()utilisé pour sélectionner des objets spécifiques mais exclude()utilisé pour supprimer

Muhammed Faes EP
la source