Django LEFT JOIN? - PullRequest
       8

Django LEFT JOIN?

9 голосов
/ 14 сентября 2009

У меня есть модели, более или менее такие:

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 ИЛИ без имени модели вообще» *

Пока у меня есть это:

ModelA.objects.exclude(field=condition).filter(modelsbs__name=condition)

Это даст мне все ModelAs, у которых есть хотя бы одна модель B (и на самом деле она будет ВСЕГДА только одной), но если ModelA не имеет связанных ModelB, она не будет в наборе результатов. Мне нужно, чтобы это было в наборе результатов с чем-то вроде obj.modelb = Нет

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

Ответы [ 4 ]

11 голосов
/ 14 сентября 2009

Используйте 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()

В аналогичном запросе создается ЛЕВОЕ НАРУЖНОЕ СОЕДИНЕНИЕ ... ГДЕ (a.val = b ИЛИ.

1 голос
/ 16 марта 2012

Попробуйте этот патч для пользовательских объединений: https://code.djangoproject.com/ticket/7231

1 голос
/ 16 сентября 2009

Похоже, вы натолкнулись на 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;
0 голосов
/ 14 сентября 2009

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 выглядят как «одиночный» запрос.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...