Джанго - найти экстремального члена каждой группы - PullRequest
1 голос
/ 22 июля 2009

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

Итак, допустим, у меня есть следующие модели -

class ContactGroup(models.Model):
    .... whatever ....

class Contact(models.Model):
    group = models.ForeignKey(ContactGroup)
    name = models.CharField(max_length=20)
    email = models.EmailField()
...

class Record(models.Model):
    contact = models.ForeignKey(Contact)
    group = models.ForeignKey(ContactGroup)
    record_date = models.DateTimeField(default=datetime.datetime.now)

    ... name, email, and other fields that are in Contact ...

Таким образом, каждый раз, когда создается или изменяется контакт, создается новая запись, которая сохраняет информацию в том виде, в котором она появилась в контакте, вместе с отметкой времени. Теперь я хочу запрос, который, например, возвращает самый последний экземпляр Record для каждого Contact, связанного с ContactGroup. В псевдокоде:

group = ContactGroup.objects.get(...)
records_i_want = group.record_set.most_recent_record_for_every_contact()

Как только я выясню это, я просто хочу иметь возможность добавить filter(record_date__lt=some_date) в набор запросов и получить информацию, существовавшую в some_date.

У кого-нибудь есть идеи?

edit: Кажется, я не совсем проясняю. Используя модели, подобные этим, я хочу сделать следующее с чистым django ORM (без лишних ()):

ContactGroup.record_set.extra(where=["history_date = (select max(history_date) from app_record r where r.id=app_record.id and r.history_date <= '2009-07-18')"])

Помещение подзапроса в предложении where является лишь одной стратегией для решения этой проблемы, остальные довольно хорошо описаны в первой ссылке, которую я дал выше. Я знаю, что подвыборы where-clause не возможны без использования extra (), но я подумал, что, возможно, один из других способов стал возможен благодаря новым функциям агрегирования.

Ответы [ 2 ]

0 голосов
/ 25 июля 2009

Прежде всего, я укажу, что:

ContactGroup.record_set.extra(where=["history_date = (select max(history_date) from app_record r where r.id=app_record.id and r.history_date <= '2009-07-18')"])

не даст вам такой же эффект, как:

records_i_want = group.record_set.most_recent_record_for_every_contact()

Первый запрос возвращает каждую запись, связанную с определенной группой (или связанную с любым из контактов определенной группы), у которой запись-дата меньше, чем дата / время, указанные в дополнительном сообщении. Запустите это в оболочке, а затем сделайте это, чтобы просмотреть запрос, созданный django:

from django.db import connection
connection.queries[-1]

, который показывает:

'SELECT "contacts_record"."id", "contacts_record"."contact_id", "contacts_record"."group_id", "contacts_record"."record_date", "contacts_record"."name", "contacts_record"."email" FROM "contacts_record" WHERE "contacts_record"."group_id" = 1  AND record_date = (select max(record_date) from contacts_record r where r.id=contacts_record.id and r.record_date <= \'2009-07-18\')

Не совсем то, что вы хотите, верно?

Теперь функция агрегирования используется для извлечения агрегированных данных, а не объектов, связанных с агрегированными данными. Поэтому, если вы пытаетесь свести к минимуму количество запросов, выполняемых с использованием агрегации, при попытке получить group.record_set.most_recent_record_for_every_contact () , у вас ничего не получится.

Без использования агрегации вы можете получить самую последнюю запись для всех контактов, связанных с группой, используя:

[x.record_set.all().order_by('-record_date')[0] for x in group.contact_set.all()]

Используя агрегацию, я смог найти следующее:

group.record_set.values('contact').annotate(latest_date=Max('record_date'))

Последний возвращает список словарей, таких как:

[{'contact': 1, 'latest_date': somedate }, {'contact': 2, 'latest_date': somedate }]

Таким образом, одна запись для каждого контакта в данной группе и даты последней записи, связанной с ним.

В любом случае, минимальный номер запроса, вероятно, составляет 1 + # контактов в группе. Если вам интересно получить результат с помощью одного запроса, это также возможно, но вам придется конструировать свои модели по-другому. Но это совсем другой аспект вашей проблемы.

Надеюсь, это поможет вам понять, как решить проблему с помощью агрегации / обычных функций ORM.

0 голосов
/ 22 июля 2009

Похоже, вы хотите вести учет изменений объектов в Django.

Pro Django имеет раздел в главе 11 (Улучшение приложений), в котором автор показывает, как создать модель, использующую другую модель в качестве клиента, которую он отслеживает для вставки / удаления / обновления. генерируется динамически из определения клиента и опирается на сигналы. В коде показана функция most_recent (), но вы можете адаптировать ее для получения состояния объекта на определенную дату.

Полагаю, проблематично отслеживание в Django, а не SQL для его получения, верно?

...