class Tag(models.Model):
name = models.CharField(maxlength=100)
class Blog(models.Model):
name = models.CharField(maxlength=100)
tags = models.ManyToManyField(Tag)
Простые модели, чтобы задать свой вопрос.
Интересно, как я могу запрашивать блоги с помощью тегов двумя разными способами.
Blog.objects.filter(tags_in=[1,2]).distinct()Tag and Blog используется только для примера.
Это больше проблема предложения или в предложении where, а не фактического объединения SQL. Если вы ищете профсоюз, посмотрите stackoverflow.com/questions/4411049/…






Вы можете использовать объекты 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 дважды.
Что касается динамического применения запроса «and» - просто перебирайте теги и накапливайте фильтрацию, используя: query = query.filter (tags__name = 'tagN')
Я думаю, что первый пример помогает. Я размышлял, нужен ли отчетливый. В терминах SQL у вас будет 2 присоединения блога к BlogTagLink и BlogTagLink для пометки, чтобы данная запись блога была указана несколько раз в результирующем наборе.
Пожалуйста, не изобретайте велосипед и используйте приложение для создания тегов django, который был создан именно для вашего варианта использования. Он может выполнять все описанные вами запросы и многое другое.
Если вам нужно добавить настраиваемые поля в модель тегов, вы также можете взглянуть на моя ветка django-tagging.
Это поможет вам
Blog.objects.filter(tags__name__in=['tag1', 'tag2']).annotate(tag_matches=models.Count(tags)).filter(tag_matches=2)
Проверьте этот вопрос с действительно отличным ответом. Может быть полезно (я знаю, что этому вопросу ~ 6 лет, но я все еще нашел его, пока искал ответы!)