Django中复杂Q对象过滤器查询的奇怪行为

时间:2010-04-27 02:26:48

标签: django search filter django-q

您好我正在尝试为Django编写标记系统,但今天我在filter或Q对象(django.db.models.Q)中遇到了一个奇怪的行为。

我写了一个函数,它将搜索字符串转换为Q对象。下一步是使用这些查询过​​滤TaggedObject。但不幸的是,我得到了一个奇怪的行为。

仅搜索一个Tag元素:

当我搜索(id=20) => Q: (AND: ('tags__tag__id', 20)) 它返回2个具有ID 1127和132

的Taged对象

当我搜索(id=4) => Q: (AND: ('tags__tag__id', 4)) 它还返回2个对象,但这次是1180和1127

这是重新设置的SQL查询:

SELECT "django_content_type"."id", "django_content_type"."name", "django_content_type"."app_label", "django_content_type"."model" 
FROM "django_content_type" 
WHERE ("django_content_type"."model" = slogan  AND "django_content_type"."app_label" = slogans ) 
ORDER BY "django_content_type"."name" ASC

SELECT "slogans_slogan"."id", "slogans_slogan"."headline", "slogans_slogan"."text", "slogans_slogan"."author"
FROM "slogans_slogan"
  INNER JOIN "htags_objecttagbridge" ON ("slogans_slogan"."id" = "htags_objecttagbridge"."object_id")
WHERE ("htags_objecttagbridge"."tag_id" = 4  AND "htags_objecttagbridge"."content_type_id" = 9 )
LIMIT 21

使用'或'结合搜索两个标记:

直到这里一切都很好,但当我做一些更复杂的查询,如(id=4) or (id=20) => Q: (OR: ('tags__tag__id', 4), ('tags__tag__id', 20)) 然后它返回4(!)对象1180,1127,1127,132

和SQL:

SELECT "slogans_slogan"."id", "slogans_slogan"."headline", "slogans_slogan"."text", "slogans_slogan"."author"
FROM "slogans_slogan"
  INNER JOIN "htags_objecttagbridge" ON ("slogans_slogan"."id" = "htags_objecttagbridge"."object_id")
WHERE ((("htags_objecttagbridge"."tag_id" = 4 AND "htags_objecttagbridge"."content_type_id" = 9 ) OR "htags_objecttagbridge"."tag_id" = 20 ) AND "htags_objecttagbridge"."content_type_id" = 9 )
LIMIT 21

但ID为1127的对象会返回两次,但这不是我想要的行为。我是否必须忍受它,并将该列表统一起来,或者我可以做些不同的事情。 Q对象的表示对我来说很好。

搜索两个标签'和'连接

但现在最糟糕的是,当我搜索(id=20) and (id=4) => Q: (AND: ('tags__tag__id', 20), ('tags__tag__id', 4))然后它根本不返回任何对象。但为什么?表示应该是正确的,并且具有id 1127的对象被两者标记。我错过了什么?

这里又是SQL:

SELECT "slogans_slogan"."id", "slogans_slogan"."headline", "slogans_slogan"."text", "slogans_slogan"."author" 
FROM "slogans_slogan"
  INNER JOIN "htags_objecttagbridge" ON ("slogans_slogan"."id" = "htags_objecttagbridge"."object_id")
WHERE ("htags_objecttagbridge"."tag_id" = 4  AND "htags_objecttagbridge"."content_type_id" = 9  AND "htags_objecttagbridge"."tag_id" = 20 )
LIMIT 21

[编辑]: 我现在意识到,这个SQL语句是错误的。至少不是我想要的,因为在这里它想要的是,一个ObjectTagBridge具有id 4并且同时具有id 20.但在我的情况下这些是2个不同的

以下是涉及的类的相关部分:

class TaggedObject(models.Model):
    """
        class that represent a tagged object
    """
    tags = generic.GenericRelation('ObjectTagBridge',
                                   blank=True, null=True)

class ObjectTagBridge(models.Model):
    """
        Help to connect a generic object to a Tag.
    """
    # pylint: disable-msg=W0232,R0903
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    tag = models.ForeignKey('Tag')

class Tag(models.Model):
    ...

感谢您的帮助

1 个答案:

答案 0 :(得分:5)

对于问题1(唯一性):您将要进行查询distinct。复制是该类型查询的预期行为,除非您将其区分开来。

对于问题2,您可能会遇到查询集如何工作的微妙但重要的部分。如果你这样做一个查询:

mymodel.objects.filter(tags__tag__id=4, tags__tag__id=5)

您正在查询具有标记的模型,该标记同时具有id = 4和id = 5,这当然没有标记。但如果您改为这样查询:

mymodel.objects.filter(tags__tag__id=4).filter(tags__tag__id=5)

你得到的模型在id = 4的地方有一些标签,而某些标签的id = 5。 Q对象也是如此 - 它们需要分成单独的filterexclude调用,而不是指单个Tag关系。这是documented here