2009-09-14 2 views
9

У меня есть модели, более или менее, как это: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') 

Я хочу, чтобы сделать запрос, который говорит что-то вроде: «Получить все Modela, где имя поля равна X, которые имеют модель ModelB с именем X OR без названия модели на всех»

до сих пор у меня есть это:

Это поможет мне все ModelAs, которые имеют по крайней мере один modelB (а в действительности это будет ВСЕГДА быть только один) - но если ModelA не имеет соответствующих ModelBs, это не будет t в результирующем наборе. Мне нужно, чтобы он был в наборе результатов с чем-то вроде obj.modelb = None

Как это сделать?

+3

На стороне записки: что бы реально помочь, если вы использовали описательные имена, как типичный Блог/Post сценария или по крайней мере, Foo/Bar вместо ModelA/ModelB, которые не интуитивно понятны и просто трудно читать/различать. –

ответ

11

Используйте Q, чтобы объединить два условия:

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

Чтобы исследовать результирующий SQL запрос:

print qs.query.as_sql() 

На аналогичном запросе, это создает левое внешнее соединение ... где (а .val = b ИЛИ a.id IS NULL).

+0

Это не имеет значения для меня – 2009-09-14 03:56:57

+3

Если все, что вы собираетесь сказать, это «это не работает», я, конечно, не могу вам помочь. Вы даже изучали SQL? –

+0

Да, я это сделал. Извините, я не был более конкретным. Я был занят попыткой предложения, которое я нашел в другом месте. Во всяком случае, делать то, что у вас есть, заключается в том, чтобы сделать условие соединения на предложение where в отличие от предложения ON, я не совсем уверен, почему, но это заставляет левое соединение не вести себя так, как ожидалось. Если я вручную запускаю тот же запрос с условием, перемещенным в ON, он работает так, как я хочу. – 2009-09-14 04:33:42

-3

LEFT JOIN - это объединение двух запросов. Иногда он оптимизирован для одного запроса. Иногда это фактически не оптимизируется базовым механизмом SQL и выполняется как два отдельных запроса.

Сделайте это.

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 

Не фетишизируйте внешние соединения SQL, являющиеся «единственным» запросом.

+5

Это лет, но я хочу отметить, страшный совет. Это заканчивается запуском огромного количества запросов за кулисами и может легко привести ваш сайт в обход. –

1

Похоже, вы приближаетесь к 80% -му барьеру. Почему бы просто не использовать .extra(select={'has_x_or_none':'(EXISTS (SELECT ...))'}) для выполнения подзапроса? Вы можете написать подзапрос любым способом, который вам нравится, и иметь возможность фильтровать его в новом поле. SQL должен заводиться выглядеть примерно так:

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;