Django LEFT OUTER JOIN для двух столбцов, где один не является внешним ключом

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

class ObjectLock(models.Model):
    partner = models.ForeignKey(Partner)
    object_id = models.CharField(max_length=100)

    class Meta:
        unique_together = (('partner', 'object_id'),)

class ObjectImportQueue(models.Model):
    partner = models.ForeignKey(Partner)
    object_id = models.CharField(max_length=100)
    ... # other fields
    created = models.DateTimeField(auto_now_add = True)
    modified = models.DateTimeField(auto_now = True, db_index=True)

    class Meta:
        ordering = ('modified', 'created')

В третьей упомянутой выше модели (Partner) нет ничего примечательного.

Я хотел бы получить что-то вроде:

SELECT * FROM ObjectImportQueue q LEFT OUTER JOIN ObjectLock l ON
q.partner_id=l.partner_id AND q.object_id=l.object_id WHERE l.object_id
IS NULL and l.partner_id IS NULL;

Я наткнулся на эту страницу, рассказывает, как выполнять настраиваемые соединения, и я попытался передать кортеж имен столбцов для соединения вместо имени столбца для соединения, но это не сработало. Таблицу Partner не нужно включать в результирующий SQL-запрос, но я приму ответ, который включает ее, если он эффективно выполняет то, что я пытаюсь сделать с одним запросом.


person Eddified    schedule 27.09.2010    source источник
comment
Может я тут что-то напутал, но почему бы не поискать object_id=NULL и partner_id=NULL без присоединения? должны дать вам те же результаты?   -  person Bernhard Vallant    schedule 28.09.2010
comment
Я пытаюсь найти строки в таблице очереди, которым нет соответствующей строки в таблице блокировок.   -  person Eddified    schedule 28.09.2010


Ответы (3)


Я также встречаю аналогичный вопрос. Но, наконец, я обнаружил, что задал неправильный вопрос, который нужно решить.

в Django ORM условие соединения SQL основано на том, что определено в полях models.Model.

существуют отношения «многие к одному» (ForeignKey), отношения «многие ко многим» (ManyToManyField), отношения «один к одному» (OneToOneField).

в вашей ситуации. ObjectLockModel и ObjectImportQueueModel имеют одинаковую часть полей, partnerfield и object_idfield.yon должны использовать отношения «один к одному».

вы можете изменить свою модель следующим образом:

class ObjectImportQueue(models.Model):
    partner = models.ForeignKey(Partner)
    object_id = models.CharField(max_length=100)
    created = models.DateTimeField(auto_now_add = True)
    modified = models.DateTimeField(auto_now = True, db_index=True)

    def __unicode__(self):
        return u"%s:%s" % (self.partner, self.object_id)

    class Meta:
        ordering = ('modified', 'created')

class ObjectLock(models.Model):
    lock = models.OneToOneField(ObjectImportQueue, null=True)

    class Meta:
        unique_together = (('lock',),)

порядок модели - импорт, OneToOneField модель аргумента должна стоять первой.

>>> p1 = Partner.objects.get(pk=1)
>>> p2 = Partner.objects.get(pk=2)
>>> Q1 = ObjectImportQueue.objects.create(partner=p1,object_id='id_Q1')
>>> Q2 = ObjectImportQueue.objects.create(partner=p2,object_id='id_Q2')
>>> ObjectImportQueue.objects.filter(lock__isnull=True)
[<ObjectImportQueue: Partner object:id_Q1>, <ObjectImportQueue: Partner object:id_Q2>]
>>> L1 = ObjectLock.objects.create(lock=Q1)
>>> ObjectImportQueue.objects.filter(lock__isnull=True)
[<ObjectImportQueue: Partner object:id_Q2>]

ObjectLock.objects.createзаблокировать объект ObjectImportQueue.objects.filter(lock__isnull=True) выбрать объект не блокировать.

если вы используете соответствующие отношения, сгенерировать запрос ORM будет легко. В Django,Определить отношения во время построения модели лучше, чем использовать оператор запроса для связи отношений между таблицами.

person xavierskip    schedule 08.12.2015
comment
Думаю, лучший ответ для моего конкретного случая. Но я задал вопрос так давно, что не знаю, будет ли он на самом деле работать, но я полагаю, что должен. Отметить как ответ. Это не лучший общий ответ для левого внешнего соединения двух столбцов. - person Eddified; 08.12.2015
comment
stackoverflow.com/questions/29711547/ может быть это полезно - person xavierskip; 09.12.2015

Если вы используете Django 1.2+ и знаете нужный SQL, вы всегда можете вернуться к Необработанный запрос.

person Tom    schedule 05.11.2010

Я только что нашел решение этой проблемы.

Вы должны создать представление, которое выполняет объединение для вас

CREATE VIEW ImporQueueLock AS (
  SELECT q.id, l.id
    FROM ObjectImportQueue q
    LEFT OUTER JOIN ObjectLock l
        ON q.partner_id=l.partner_id AND q.object_id=l.object_id
  )

Затем создайте модель django для этого представления.

class ImportQueueLock(models.Model):

    queue = models.ForeignKey(ObjectImportQueue, db_column='q')
    lock = models.ForeignKey(ObjectLock, db_column='l')

Затем сделайте ManyToMany в вашей модели Django из ObjectLock в ObjectImportQueue через ImportQueueLock.

class ObjectLock(models.Model):
    partner = models.ForeignKey(Partner)
    object_id = models.CharField(max_length=100)
    queue = models.ManyToManyField(ObjectImportQueue, through = ImportQueueLock)

и вы сможете сделать

ObjectLock.objects.filter(importqueuelock__objectimportqueue__ .....)
person Ghislain Leveque    schedule 12.10.2012