Странное поведение со сложными запросами фильтра объектов Q в Django - PullRequest
1 голос
/ 27 апреля 2010

Привет! Я пытаюсь написать систему тегов для Django, но сегодня я столкнулся со странным поведением в фильтре или объекте Q (django.db.models.Q).

Я написал функцию, которая преобразует строку поиска в объект Q. Следующим шагом будет фильтрация TaggedObject по этим запросам. Но, к сожалению, у меня странное поведение.

поиск только одного элемента тега:

когда я ищу (id=20) => Q: (AND: ('tags__tag__id', 20)) и возвращает 2 объекта Taged с идентификаторами 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

Но объект с идентификатором 1127 возвращается дважды, но это не то поведение, которое мне нужно. Должен ли я жить с этим и унифицировать этот список, или я могу сделать что-то другое? Представление объекта Q выглядит хорошо для меня.

поиск по двум тегам и соединению

Но самое страшное сейчас, когда я ищу (id=20) and (id=4) => Q: (AND: ('tags__tag__id', 20), ('tags__tag__id', 4)) тогда он вообще не возвращает никаких объектов. Но почему? Представление должно быть в порядке, и объект с идентификатором 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 имел идентификатор 4 и одновременно идентификатор 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 Ответ

5 голосов
/ 01 мая 2010

Для задачи 1 (уникальность): вы хотите, чтобы ваш запрос отличался . Дублирование является ожидаемым поведением для этого типа запроса, если вы не сделаете его отличным.

Для проблемы 2 вы, вероятно, сталкиваетесь с тонкой, но важной частью работы наборов запросов. Если вы сделаете запрос, подобный этому:

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

Вы запрашиваете модель с одиночным тегом , который имеет оба идентификатора = 4 и идентификатор = 5, что, конечно, не является тегом. Но если вы вместо этого запросите, как это:

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

вы получаете модели, у которых где-то есть тег с id = 4 и где-то тег с id = 5. То же самое относится и к объектам Q - их нужно будет разделить на отдельные вызовы filter или exclude, чтобы не ссылаться на одно отношение Tag. Это задокументировано здесь .

...