Союз и пересечение в Django

class Tag(models.Model):
  name = models.CharField(maxlength=100)

class Blog(models.Model):
  name = models.CharField(maxlength=100)
  tags =  models.ManyToManyField(Tag)

Простые модели, чтобы задать свой вопрос.

Интересно, как я могу запрашивать блоги с помощью тегов двумя разными способами.

  • Записи блога, помеченные тегами "tag1" или "tag2": Blog.objects.filter(tags_in=[1,2]).distinct()
  • Объекты блога, помеченные тегами "tag1" и "tag2": ?
  • Объекты блога, которые помечены как «tag1» и «tag2», и никак иначе: ??

Tag and Blog используется только для примера.

Проверьте этот вопрос с действительно отличным ответом. Может быть полезно (я знаю, что этому вопросу ~ 6 лет, но я все еще нашел его, пока искал ответы!)

gregoltsov 26.02.2014 22:33

Это больше проблема предложения или в предложении where, а не фактического объединения SQL. Если вы ищете профсоюз, посмотрите stackoverflow.com/questions/4411049/…

jocassid 14.07.2017 01:30
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
32
2
15 466
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Вы можете использовать объекты Q для №1:

# Blogs who have either hockey or django tags.
from django.db.models import Q
Blog.objects.filter(
    Q(tags__name__iexact='hockey') | Q(tags__name__iexact='django')
)

Я считаю, что союзы и пересечения немного выходят за рамки Django ORM, но это возможно. Следующие примеры взяты из приложения Django под названием django-tagging, которое обеспечивает эту функциональность. Строка 346 из models.py:

Во второй части вы ищете объединение двух запросов, в основном

def get_union_by_model(self, queryset_or_model, tags):
    """
    Create a ``QuerySet`` containing instances of the specified
    model associated with *any* of the given list of tags.
    """
    tags = get_tag_list(tags)
    tag_count = len(tags)
    queryset, model = get_queryset_and_model(queryset_or_model)

    if not tag_count:
        return model._default_manager.none()

    model_table = qn(model._meta.db_table)
    # This query selects the ids of all objects which have any of
    # the given tags.
    query = """
    SELECT %(model_pk)s
    FROM %(model)s, %(tagged_item)s
    WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
      AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
      AND %(model_pk)s = %(tagged_item)s.object_id
    GROUP BY %(model_pk)s""" % {
        'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
        'model': model_table,
        'tagged_item': qn(self.model._meta.db_table),
        'content_type_id': ContentType.objects.get_for_model(model).pk,
        'tag_id_placeholders': ','.join(['%s'] * tag_count),
    }

    cursor = connection.cursor()
    cursor.execute(query, [tag.pk for tag in tags])
    object_ids = [row[0] for row in cursor.fetchall()]
    if len(object_ids) > 0:
        return queryset.filter(pk__in=object_ids)
    else:
        return model._default_manager.none()

Что касается части №3, я думаю, вы ищете перекресток. См. строка 307 из models.py

def get_intersection_by_model(self, queryset_or_model, tags):
    """
    Create a ``QuerySet`` containing instances of the specified
    model associated with *all* of the given list of tags.
    """
    tags = get_tag_list(tags)
    tag_count = len(tags)
    queryset, model = get_queryset_and_model(queryset_or_model)

    if not tag_count:
        return model._default_manager.none()

    model_table = qn(model._meta.db_table)
    # This query selects the ids of all objects which have all the
    # given tags.
    query = """
    SELECT %(model_pk)s
    FROM %(model)s, %(tagged_item)s
    WHERE %(tagged_item)s.content_type_id = %(content_type_id)s
      AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s)
      AND %(model_pk)s = %(tagged_item)s.object_id
    GROUP BY %(model_pk)s
    HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % {
        'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)),
        'model': model_table,
        'tagged_item': qn(self.model._meta.db_table),
        'content_type_id': ContentType.objects.get_for_model(model).pk,
        'tag_id_placeholders': ','.join(['%s'] * tag_count),
        'tag_count': tag_count,
    }

    cursor = connection.cursor()
    cursor.execute(query, [tag.pk for tag in tags])
    object_ids = [row[0] for row in cursor.fetchall()]
    if len(object_ids) > 0:
        return queryset.filter(pk__in=object_ids)
    else:
        return model._default_manager.none()

Я тестировал их с помощью Django 1.0:

Запросы "или":

Blog.objects.filter(tags__name__in=['tag1', 'tag2']).distinct()

или вы можете использовать класс Q:

Blog.objects.filter(Q(tags__name='tag1') | Q(tags__name='tag2')).distinct()

Запрос "и":

Blog.objects.filter(tags__name='tag1').filter(tags__name='tag2')

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

Хм, этот запрос "and" выглядит как удобный трюк, за исключением того, что вначале вы не будете знать, сколько раз потребуется применить .filter. Пользователь может искать dog + goat + cat, и в этом случае вам понадобится .filter дважды.

mlissner 06.06.2011 03:37

Что касается динамического применения запроса «and» - просто перебирайте теги и накапливайте фильтрацию, используя: query = query.filter (tags__name = 'tagN')

Lukasz 15.08.2016 21:58

Я думаю, что первый пример помогает. Я размышлял, нужен ли отчетливый. В терминах SQL у вас будет 2 присоединения блога к BlogTagLink и BlogTagLink для пометки, чтобы данная запись блога была указана несколько раз в результирующем наборе.

jocassid 14.07.2017 01:28

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

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

Это поможет вам

Blog.objects.filter(tags__name__in=['tag1', 'tag2']).annotate(tag_matches=models.Count(tags)).filter(tag_matches=2)

Другие вопросы по теме