代码之家  ›  专栏  ›  技术社区  ›  HWM-Rocker

django中复杂q对象过滤查询的奇怪行为

  •  1
  • HWM-Rocker  · 技术社区  · 14 年前

    嗨,我试图为django编写一个标记系统,但今天在filter或q对象(django.db.models.q)中遇到了一个奇怪的行为。

    我编写了一个函数,将搜索字符串转换为q对象。下一步是用这些查询过滤taggedobject。但不幸的是我有一个奇怪的行为。

    只搜索一个标记元素:

    当我搜索时 (id=20) = & gt; Q: (AND: ('tags__tag__id', 20)) 它返回两个带标记的对象,id为1127和132

    当我搜索时 (id=4) = & gt; Q: (AND: ('tags__tag__id', 4)) 它还返回两个对象,但这次是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) = & gt; 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对象的表示对我来说很好。

    搜索两个标记“and”连词

    但最糟糕的是,当我寻找 (id=20) and (id=4) = & gt; Q: (AND: ('tags__tag__id', 20), ('tags__tag__id', 4)) 然后它根本不返回任何对象。但是为什么呢?表示应该是OK的,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。但在我的情况下这是两个不同的

    以下是课程的相关部分:

    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 回复  |  直到 9 年前
        1
  •  5
  •   user85461    14 年前

    对于问题1(唯一性):您将要进行查询 distinct . 重复是该类型查询的预期行为,除非您使其不同。

    对于问题2,您可能碰到了QuerySets工作方式中一个微妙但重要的部分。如果您这样查询:

    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对象也是一样的——它们需要分开 filter exclude 不引用单个标记关系的调用。这是 documented here .

    推荐文章