2016-03-11 5 views
1

У меня есть следующие модели:Сравнение элементов через коллекции

class Collection(models.Model): 
    ... 

class Record(models.Model): 
    collection = models.ForeignKey(Collection, related_name='records') 
    filename = models.CharField(max_length=256) 
    checksum = models.CharField(max_length=64) 

    class Meta: 
     unique_together = (('filename', 'collection'),) 

Я хочу, чтобы выполнить следующий запрос:

Для каждого filename из Record Я хочу знать Collections, что:

  • Не указывать Record с этим именем файла
  • или, которые обеспечивают такую ​​Record но имеет различающиеся checksum

Я имею в виду выход так:

  | C1 C2 C3 <- collections 
-----------+------------ 
file-1.txt |  x  
file-2.txt |  x  
file-3.txt | ! ! ! 
file-4.txt | x ! ! 
file-5.txt | ! ! x 

x = missing 
! = different checksum 

Что я ком до сих пор является то, что я создаю запрос для каждый Collection, за исключением всех имен файлов, которые находятся в этой коллекции, но существуют в других.

for collection in collections: 
    other_collections = [c for c in collections if c is not collection] 
    results[collection] = qs.filter(collection__in=other_collections).exclude(
     filename__in=qs.filter(
      collection=collection 
     ).values_list('filename', flat=True) 
    ).order_by('filename').values_list('filename', flat=True) 

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

Возможно ли выполнить два запроса в одном комбинированном шаге, чтобы получить результаты в формате, описанном выше?

Решение не обязательно должно использовать API-интерфейсы QuerySet, откат к необработанному SQL мне тоже подходит.

ответ

1

Невозможно написать SQL-запрос, который возвращает переменное количество столбцов, хотя вы можете добиться этого эффекта, если обернуть все в an array или объект JSON.

Если вы знаете коллекции, вы можете написать SQL, как это:

SELECT r.filename, 
     (SELECT r.checksum = r2.checksum FROM records r2 WHERE r.filename = r2.filename AND r2.collection_id = 1) AS c1, 
     (SELECT r.checksum = r2.checksum FROM records r2 WHERE r.filename = r2.filename AND r2.collection_id = 2) AS c2, 
     ... 
FROM records r 
WHERE r.collection_id = 1 
GROUP BY r.filename, r.checksum 

Для каждой пары имя файла/коллекции, вы получите NULL, если коллекция не имеет записи, true, если коллекция имеет он с правильной контрольной суммой, или false, если коллекция имеет его с другой контрольной суммой.

Включая WHERE r.collection_id = 1, потому что в противном случае для сравнения контрольной суммы вы должны ответить «отличным от чего?».

Смежные вопросы