Django LEFT OUTER JOIN на двух столбцах, где один не является внешним ключом - PullRequest
3 голосов
/ 28 сентября 2010

У меня есть две модели, подобные этой:

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')

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

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

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;

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

Ответы [ 3 ]

1 голос
/ 08 декабря 2015

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

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

есть отношения многие-к-одному (ForeignKey), отношения многие-ко-многим (ManyToManyField), отношения один-к-одному (OneToOneField).

в вашей ситуации. ObjectLock Модель и ObjectImportQueue Модель имеют одинаковую часть полей, поле partner и object_id field.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',),)

порядок Model является импортом, 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 , Определить отношения во время построения модели лучше, чем использовать оператор Query для связи между таблицами.

1 голос
/ 05 ноября 2010

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

0 голосов
/ 12 октября 2012

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

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

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__ .....)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...