Поиск нескольких экземпляров тега с помощью «сквозного» поля Django - PullRequest
2 голосов
/ 17 декабря 2009

Я управляю веб-сайтом лабораторных аннотаций, где пользователи могут комментировать образцы тегами, относящимися к болезни, типу ткани и т. Д. Вот простой пример из models.py:

from django.contrib.auth.models import User
from django.db import models


class Sample(models.Model):
    name = models.CharField(max_length = 255)
    tags=models.ManyToManyField('Tag', through = 'Annot')

class Tag(models.Model):
    name = models.CharField(max_length = 255)

class Annot(models.Model):
    tag = models.ForeignKey('Tag')
    sample = models.ForeignKey('Sample')
    user = models.ForeignKey(User, null = True)
    date = models.DateField(auto_now_add = True)

Я ищу запрос в ORM django, который вернет теги, в которых два пользователя согласны с аннотацией к одному тегу. Было бы полезно, если бы я мог предоставить список пользователей для ограничения моего запроса (если кто-то верит только в User1 и User2 и хочет найти пары примеров / тегов, с которыми согласны только они.)

1 Ответ

1 голос
/ 17 декабря 2009

Мне кажется, я понял, что тебе нужно. Это заставило меня задуматься, спасибо! : -)

Я считаю, что эквивалентный SQL-запрос будет выглядеть примерно так:

select t.name, s.name, count(user_id) count_of_users
  from yourapp_annot a, yourapp_tag t, yourapp_sample s 
 where a.tag_id = t.id 
   and s.id = a.sample_id
group by t.name, s.name
having count_of_users > 1

Хотя я стараюсь не думать о SQL, когда я придумываю навигацию по моделям django (это мешает); когда дело доходит до запросов агрегации, это всегда помогает мне увидеть, каким будет SQL.

В django у нас теперь есть агрегатов .

Вот что я придумал:

models.Annot.objects.select_related().values(
  'tag__name','sample__name').annotate(
  count_of_users=Count('user__id')).filter(count_of_users__gt=1)

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

Разбить его на части для людей, которые не привыкли к агрегированию django:

models.Annot.objects.select_related()

  • select_related () принудительно запрашивает все таблицы, относящиеся к Annot, в одном запросе
  • Это то, что позволит мне указать tag__name и sample__name в значениях (), вызов

values('tag__name','sample__name')

  • values ​​() ограничивает поля для извлечения до tag.name и sample.name
  • Это гарантирует, что мое агрегирование по количеству клиентов будет группироваться только по этим полям

annotate(count_of_users=Count('user__id'))

  • annotate () добавляет агрегацию в качестве дополнительного поля к запросу

filter(count_of_users__gt=1)

  • И, наконец, я фильтрую по совокупному количеству.

Если вы хотите добавить дополнительный фильтр на то, какие пользователи должны учитываться, вам нужно сделать это:

models.Annot.objects.filter(user=[... list of users...]).select_related().values(
  'tag__name','sample__name').annotate(
  count_of_users=Count('user__id')).filter(count_of_users__gt=1)

Я думаю, что это так.


Одна вещь ... Обратите внимание, что я использовал tag__name и sample__name в запросе выше. Но ваши модели не указывают, что имена тегов и имена образцов уникальны .

Должны ли они быть уникальными? Добавьте unique=True к определениям полей в моделях.

Разве они не должны быть уникальными? Вам нужно заменить tag__name и sample__name на tag__id и sample__id в запросе выше.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...