【问题标题】:Strange behavior with complex Q object filter queries in DjangoDjango 中复杂 Q 对象过滤器查询的奇怪行为
【发布时间】:2011-02-12 16:25:52
【问题描述】:

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

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

只搜索一个标签元素:

当我搜索(id=20) => Q: (AND: ('tags__tag__id', 20)) 它返回 2 个 ID 为 1127 和 132 的标记对象

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

这里是 reluting 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 对象的表示对我来说看起来不错。

搜索两个标签'and'连词

但最糟糕的是现在,当我搜索 (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):
    ...

感谢您的帮助

【问题讨论】:

  • 你能显示这些查询的 _as_sql() 结果吗?

标签: django search filter django-q


【解决方案1】:

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

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

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

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

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

你得到的模型在某处有一些 id=4 的标签,而在某处有一些 id=5 的标签。 Q 对象也是如此——它们需要被拆分为单独的filterexclude 调用才能不引用单个标记关系。这是documented here

【讨论】:

  • 非常感谢。我已经重写了我的应用程序,但我强烈希望只需一个查询就能完成所有操作。
  • 查询集被延迟评估。因此,如果您像这样链接“过滤器”或“排除”调用,您就不会进行额外的查询——当它被评估时,它仍然会在一个 SQL 调用中发生。
猜你喜欢
  • 2019-09-17
  • 2016-02-02
  • 2010-12-31
  • 2015-01-22
  • 2018-12-19
  • 2010-12-31
  • 2012-04-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多