2009-09-14 22 views
9

tengo modelos, más o menos así:Django ¿LEFT JOIN?

class ModelA(models.Model): 
    field = models.CharField(..) 

class ModelB(models.Model): 
    name = models.CharField(.., unique=True) 
    modela = models.ForeignKey(ModelA, blank=True, related_name='modelbs') 

    class Meta: 
     unique_together = ('name','modela') 

Quiero hacer una consulta que dice algo así como: "Obtener todos los de la ModelA en nombre del campo es igual a X que tiene un modelo ModelB con un nombre de X O sin nombre del modelo en absoluto"

Hasta ahora tengo esto:

esto me todas las ModelAs que tienen al menos un ModelB (y, en realidad, siempre será sólo uno) - pero si un ModelA no tiene un ModelBs relacionado, no lo hará t estar en el conjunto de resultados. Necesito que esté en el conjunto de resultados con algo como obj.modelb = None

¿Cómo puedo lograr esto?

+3

En una nota: que realmente le ayudaría si utilizó nombres descriptivos como típico blog/o escenario post al menos Foo/Bar en lugar de ModelA/ModelB que no son intuitivos y simplemente difíciles de leer/distinguir. –

Respuesta

11

Uso Q para combinar las dos condiciones:

from django.db.models import Q 
qs = ModelA.objects.exclude(field=condition) 
qs = qs.filter(Q(modelbs__name=condition) | Q(modelbs__isnull=True)) 

para examinar la consulta SQL resultante:

print qs.query.as_sql() 

En una consulta similar, esto genera una combinación externa izquierda ... donde (a .val = b O a.id ES NULO).

+0

No hace la diferencia para mí –

+3

Si todo lo que va a decir es "no está funcionando", ciertamente no puedo ayudarlo. ¿Incluso examinaste el SQL? –

+0

Sí, lo hice. Lo siento, no fui más específico, estaba ocupado intentando una sugerencia que encontré en otro lado. De todos modos, hacer lo que tiene es hacer que la condición de unión esté en la cláusula where en oposición a la cláusula ON, no estoy exactamente seguro de por qué, pero esto hace que la combinación izquierda no se comporte como se esperaba. Si ejecuto manualmente la misma consulta con la condición movida a ON, funciona como quiero. –

-3

LEFT JOIN es una unión de dos consultas. A veces está optimizado para una consulta. A veces, en realidad no está optimizado por el motor SQL subyacente y se realiza como dos consultas separadas.

Haga esto.

for a in ModelA.objects.all(): 
    related = a.model_b.set().all() 
    if related.count() == 0: 
     # These are the A with no B's 
    else: 
     # These are the A with some B's 

No fetichice las uniones externas de SQL que parezcan ser una consulta "única".

+5

Esto tiene años, pero quiero señalar, terrible consejo. Esto termina ejecutando una gran cantidad de consultas detrás de escena, y puede hacer que su sitio avance rápidamente. –

1

Parece que se enfrenta a la barrera del 80%. ¿Por qué no usar .extra(select={'has_x_or_none':'(EXISTS (SELECT ...))'}) para realizar una subconsulta? Puede escribir la subconsulta de la manera que quiera y debería poder filtrar contra el nuevo campo. El SQL debe terminar buscando algo como esto:

SELECT *, 
    ((EXISTS (SELECT * FROM other WHERE other.id=primary.id AND other.name='X')) 
    OR (NOT EXISTS (SELECT * FROM other WHERE other.id=primary.id))) AS has_x_or_none 
    FROM primary WHERE has_x_or_none=1;