Django中的联合和相交
Posted
技术标签:
【中文标题】Django中的联合和相交【英文标题】:Union and Intersect in Django 【发布时间】:2010-09-11 14:43:12 【问题描述】: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”标记的博客对象:??
标签和博客只是一个例子。
【问题讨论】:
查看this question 的答案非常棒。可能会有所帮助(我知道这个问题已经有 6 年的历史了,但我在寻找答案时仍然找到了它!) 这更多是 where 子句中的 or 问题,而不是实际的 SQL 联合问题。如果您正在寻找工会,请查看***.com/questions/4411049/… 【参考方案1】:#1 可以使用 Q 对象:
# 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-tagging 的 Django 应用程序,它提供了该功能。 Line 346 of 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 部分,我相信您正在寻找一个十字路口。见line 307 of 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()
【讨论】:
【参考方案2】:我已经用 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 来完成。
【讨论】:
Hrm,这个“and”查询看起来很方便,只是你一开始不知道 .filter 需要应用多少次。用户可能正在寻找 dog+goat+cat,在这种情况下,您需要 .filter 两次。 关于“and”查询的动态应用——只需遍历标签并累积过滤使用:query = query.filter(tags__name='tagN') 我认为第一个例子可以解决问题。我在思考是否需要不同的。在 SQL 术语中,您将有 2 个连接 Blog to BlogTagLink 和 BlogTagLink to Tag 给定的博客记录将在结果集中多次列出。【参考方案3】:请不要重新发明***并使用 django-tagging application,它完全是为您的用例而制作的。它可以执行您描述的所有查询,等等。
如果您需要在 Tag 模型中添加自定义字段,也可以查看 my branch of django-tagging。
【讨论】:
【参考方案4】:这将为您解决问题
Blog.objects.filter(tags__name__in=['tag1', 'tag2']).annotate(tag_matches=models.Count(tags)).filter(tag_matches=2)
【讨论】:
以上是关于Django中的联合和相交的主要内容,如果未能解决你的问题,请参考以下文章