Saltar al contenido

Django queryset exclude () con múltiples cláusulas de campo relacionadas

Solución:

Este es un gotcha todavía presente en Django, donde exclude() no está actuando como el reverso de filter(). Aquí está la documentación que explica la diferencia:

Nota

El comportamiento de filter() para consultas que abarcan relaciones de varios valores, como se describe anteriormente, no se implementa de manera equivalente para
exclude(). En cambio, las condiciones en un solo exclude() la llamada no se referirá necesariamente al mismo artículo.

Por ejemplo, la siguiente consulta excluiría los blogs que contienen tanto entradas con “Lennon” en el título como entradas publicadas en 2008:

Blog.objects.exclude(
    entry__headline__contains="Lennon",
    entry__pub_date__year=2008,
)

Sin embargo, a diferencia del comportamiento al usar filter(), esto no limitará los blogs basados ​​en entradas que cumplan ambas condiciones. Para ello, es decir, para seleccionar todos los blogs que no contienen entradas publicadas con “Lennon” que se publicaron en 2008, es necesario realizar dos consultas:

Blog.objects.exclude(
    entry__in=Entry.objects.filter(
        headline__contains="Lennon",
        pub_date__year=2008,
    ),
)

Lo que ha hecho es probablemente el camino a seguir.

Implementé una solución rápida y sucia para poder seguir adelante, esperando que fuera terriblemente ineficiente; sin embargo, al inspeccionar el SQL generado, resultó no ser tan malo:

>>> User.objects.exclude(id__in=User.objects.filter(preferences__preference="PREF_A", preferences__value=True))

Pensé que el ORM cargaría los resultados de la consulta subordinada en la memoria del servidor web antes de completar (esto es un problema porque nuestra aplicación de producción tendrá millones de usuarios), pero de hecho usa correctamente una subconsulta:

>>> User.objects.exclude(id__in=User.objects.filter(preferences__preference="PREF_A", preferences__value=True)).values('id').query.sql_with_params()
(u'SELECT "sgauth_user"."id" FROM "sgauth_user" WHERE NOT ("sgauth_user"."id" IN (SELECT U0."id" FROM "sgauth_user" U0 INNER JOIN "feeds_preference" U1 ON (U0."id" = U1."user_id") WHERE (U1."preference" = %s  AND U1."value" = %s )))', ('PREF_A', True))

Estoy presentando esto como una posible respuesta, pero todavía estoy interesado si hay una forma de hacerlo con la cláusula de exclusión sencilla, o una forma de generar una consulta a través del ORM que funcione con uniones sencillas y sin subconsultas de ningún tipo.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *