1

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

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

仅搜索一个 Tag 元素:

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

当我搜索(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):
    ...

谢谢你的帮助

4

1 回答 1

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 关系。这在此处记录

于 2010-05-01T13:41:11.557 回答