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

HWM*_*ker 1 django search filter django-q

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

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

只搜索一个Tag元素:

当我搜索(id=20)=> Q: (AND: ('tags__tag__id', 20)) 并返回2个ID为1127和132的Taged Objects时

当我搜索(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
Run Code Online (Sandbox Code Playgroud)

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

直到这里一切都很好,但当我做一个更复杂的查询,如(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
Run Code Online (Sandbox Code Playgroud)

但是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
Run Code Online (Sandbox Code Playgroud)

[编辑]: 我现在意识到,这个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):
    ...
Run Code Online (Sandbox Code Playgroud)

谢谢你的帮助

use*_*461 5

对于问题1(唯一性):您将要使您的查询与众不同.复制是该类型查询的预期行为,除非您将其区分开来.

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

mymodel.objects.filter(tags__tag__id=4, tags__tag__id=5)
Run Code Online (Sandbox Code Playgroud)

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

mymodel.objects.filter(tags__tag__id=4).filter(tags__tag__id=5)
Run Code Online (Sandbox Code Playgroud)

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