2016-05-18 2 views
5

У меня есть версионная модель, которая (упрощенный) выглядит как:Что такое способ django делать подзаголовок?

Project(id, ref, version) 

unique_together(ref, version) 

Где идентификатор является автоматически генерируемым первичным ключом, исм является случайным UUID и версия представляет собой целое число увеличивается на моем приложении. Каждый раз, когда я сохраняю проект, я создаю новый экземпляр, добавляю 1 к версии и копирую ссылку на новый объект.

Следующий SQL вернет мне последнюю версию каждого проекта, выполнив подзапрос.

SELECT * FROM myapp_project WHERE (ref, version) IN 
(SELECT ref, max(version) FROM myapp_project GROUP BY ref) 

В качестве альтернативы (немного проще, возможно):

SELECT * from myapp_project p 
WHERE p.version = 
(SELECT max(version) FROM myapp_project p1 WHERE p1.ref = p.ref) 

Как достичь того же запроса с помощью ORM Джанго?

Edit: я дошел до этого -

foo = Project.objects.values('ref').annotate(version=Max('version')) 

Это дает мне то, что выглядит хорошо, если я проверить его. Как только я пытаюсь получить идентификатор с:

foo.values('id') 

кажется, выбросите исходный результат и возвращает все строки.

Редактировать больше:

работал вокруг него сейчас с .extra():

maxids = """id in (SELECT id from myapp_project p WHERE p.version = 
(SELECT max(version) FROM myapp_project p1 WHERE p1.ref = p.ref))""" 

Project.objects.all().extra(where=[maxids]) 
+0

оффтоп: с точки зрения производительности, большинство запросов будет до последней версии проектов, так что имеет смысл sligthly изменить свой подход и иметь ваш последние версии отмечены как-то. Есть несколько вариантов, самый простой - иметь None в качестве последней версии, поэтому вы можете выбрать все проекты с нулевой версией для получения последних версий, версия будет обновлена ​​при добавлении новой версии проекта или добавлении некоторого флага '- в любом случае простой фильтр предоставит вам список последних версий - большой выигрыш по производительности – Jerzyk

ответ

2

Использование in

здесь пример прямо из связанного документа

inner_qs = Blog.objects.filter(name__contains='Cheddar') 
entries = Entry.objects.filter(blog__in=inner_qs) 

Точный запрос, который вы используете, не поддерживается всеми базами данных, которые работают с Django. Например, когда он работает на postgresql, он не работает на sqlite, поэтому вам придется изменить свой подход, чтобы использовать первичный ключ вместо уникального ключа. Или используйте соединение.

Update Второй запрос можно обрабатывать более легко, но я буду воздерживаться от публикации этого здесь, поскольку это будет выглядеть как plagarizing @ anand.

+0

Как это работает, когда в разделе IN есть 2 поля? – Darren

+0

@Darren вы можете передать 'queryset' в фильтр. – anand

1

Вы можете построить его так:

from django.db.models import Max 
latest_refs_with_max_id = Project.objects.values('ref').annotate(Max('version'), Max('id')).values('id__max') 

latest_refs = [ d['id__max'] for d in latest_refs_with_max_id] 

q = Project.objects.filter(id__in=latest_refs) 

Testing

Примечание: Это будет выбрать максимальный идентификатор, в случае, если у вас есть несколько записей с одинаковым макс version и ref

+0

a good answer +1 – e4c5

+0

Первая строка дает ошибку: FieldError: Не удается разрешить ключевое слово max в поле. Присоединиться к 'id' не разрешено. – Darren

+0

@Darren вы можете указать, какую БД вы используете для бэкэнд. – anand

1

Вы можете сделать что-то вроде этого: сначала вы получите все ссылки, а затем вы получите проект с наивысшей версией для каждого ref:

projects = [] 
for ref in Project.objects.all().values_list('ref', flat=True).distinct(): 
    projects.append(Project.objects.filter(ref=ref).order_by('-version')[0]) 

Более эффективная версия:

from django.db.models import Max 
max_project_versions = Project.objects.values('ref').annotate(id_max=Max('version')).values_list('id_max', flat=True) 
projects = Project.objects.filter(id__in=max_project_versions) 
+0

Это приведет к довольно большому количеству запросов – e4c5

+0

Это зависит от количества ссылок. Если будет несколько ссылок и много проектов за реф, он будет работать отлично. – Caumons

+0

Результат должен быть набором запросов. – Darren

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