5 votes

Filtre Django Queryset pour les citations manquantes

J'ai une liste et je veux filtrer mon Queryset lorsque l'un de ces éléments se trouve dans la clé non primaire 'test' d'une table étrangère. J'écris donc quelque chose comme ceci :

test_list = ['test1', 'test2', 'test3', 'test4', 'test5']
return cls.objects.filter(reduce(lambda x, y: x | y, [models.Q(next_task__test = item) for item in test_list]))[:20]

Cela renvoie une liste vide. Lorsque je regarde la requête SQL qu'il a générée, j'obtiens :

SELECT ...
FROM ...
WHERE "job"."next_task_id" IN (test1, test2, test3, test4, test5) LIMIT 20;

Alors que ce que ça aurait dû être, c'est ça :

SELECT ...
FROM ...
WHERE "job"."next_task_id" IN ('test1', 'test2', 'test3', 'test4', 'test5') LIMIT 20;

Sans les guillemets, SQLite3 croit qu'il s'agit de noms de colonnes, et ne renvoie rien. Lorsque j'ajoute manuellement les guillemets et que j'exécute une requête SQLite3 sur la table sans Django du tout, j'obtiens les résultats souhaités. Comment faire pour que Django exécute la requête correctement ?

6voto

Hieu Nguyen Points 4825

Ce problème est assez intéressant, il semble se produire uniquement avec SQLite. Il est connu ici : https://code.djangoproject.com/ticket/14091 et dans le docs .

En fait, la requête n'est peut-être pas fausse, mais lorsque vous la récupérez avec Django, elle semble fausse :

>>> test_list = ['test1', 'test2', 'test3', 'test4', 'test5']
>>> cls.objects.filter(next_task__test__in=test_list).query.__str__()

SELECT ...
FROM ...
WHERE "job"."next_task_id" IN (test1, test2, test3, test4, test5);

Solution : si vous pensez vraiment que la requête est erronée, fournissez plus de citations pour la liste, quelque chose du genre :

>>> test_list = ["'test1'", "'test2'", "'test3'", "'test4'", "'test5'"]
>>> cls.objects.filter(next_task__test__in=test_list).query.__str__()

SELECT ...
FROM ...
WHERE "job"."next_task_id" IN ('test1', 'test2', 'test3', 'test4', 'test5');

Je m'en remettrais à la version standard de toute façon, la solution ci-dessus est trop bricolée.

1voto

Andrey St Points 101
def stringify_queryset(qs: QuerySet) -> str:
    sql, params = qs.query.sql_with_params()
    with connection.cursor() as cursor:
        cursor.execute('EXPLAIN ' + sql, params)
        raw_sql =  cursor.db.ops.last_executed_query(cursor, sql, params)
    raw_sql = raw_sql[len('EXPLAIN '):]
    return raw_sql

(extrait de https://code.djangoproject.com/ticket/17741 )

1voto

Alan Porter Points 187

J'aime beaucoup la réponse de @Andrey-St, mais un collègue a fait remarquer que cela fait un aller-retour vers la base de données pour effectuer le travail. Nous l'avons donc modifié pour qu'il suffise de récupérer la requête formatée à partir du curseur.

 def stringify_queryset(qs):
     sql, params = qs.query.sql_with_params()
     with connection.cursor() as cursor:
         return cursor.mogrify(sql, params)

(Nous utilisons psycopg2 pour Postgres -- je ne suis pas sûr si mogrify() est disponible sur d'autres moteurs de BD).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X