Django объединяет переменное количество QuerySets - PullRequest
1 голос
/ 04 июня 2011

Есть ли способ объединить неизвестное количество наборов запросов в список?

Вот мои модели:

class Item(models.Model):
    name = models.CharField(max_length=200)
    brand = models.ForeignKey(User, related_name='brand')
    tags = models.ManyToManyField(Tag, blank=True, null=True)
    def __unicode__(self):
        return self.name
    class Meta:
        ordering = ['-id']

class Tag(models.Model):
    name = models.CharField(max_length=64, unique=True)
    def __unicode__(self):
        return self.name

У меня есть два типа запросов, с которыми я работаю:

  1. items = Item.objects.filter (brands__in = бренды)

  2. items = Item.objects.filter (tags__name = '80s'). filter (tags__name = 'comedy')

Что касается второго типа запроса, пользователи могут сохранять результаты поиска (например, "комедия 80-х годов") и могут сохранять несколько запросов одновременно, поэтому мне нужно будет создать запрос для каждого поиска, который у них есть.сохранены.

Изначально я хотел попробовать создать один запрос, который будет обрабатывать оба случая (см. Объединение запросов Django и И или ИЛИ с полем ManyToMany ), но теперь я думаю, что лучший способ сделать этобыло бы объединить все запросы в список.

Мне нравится то, что @akaihola предлагает здесь: Как объединить 2 или более наборов запросов в представлении Django? , но я не могу понять, какиспользовать itertools.chain с переменным числом запросов.

Кто-нибудь знает лучший способ сделать это?

РЕДАКТИРОВАТЬ: Забыл упомянуть, что я ищу элементы, которые имеют определенный бренд ИЛИ имеют все необходимые теги.

1 Ответ

3 голосов
/ 04 июня 2011

Немного неортодоксально, но вы можете использовать рекурсию. Итак, в вашем примере:

def recursive_search(tags, results_queryset):
    if len(tags) > 0:
        result_qs = result_queryset.filter(tags_name=tags[0])
        if result_queryset.exists():
            return filter_recursion(tags[1:],result_queryset)
        else:
            return None
    return result_queryset

tags = ["comedy", "80s", "action", "thriller"]  # This can be variable
result_queryset = Item.objects.filter(brands__in=brands) # Could be Item.objects.all()
print recursive_search(tags, result_queryset)

Итак, вы начинаете со списка тегов, которые вы ищете, и набора запросов ВСЕХ ваших товаров, которые могут соответствовать вашим критериям (в данном случае мы начинаем со списка товаров определенной марки)

Затем вы рекурсивно просматриваете список тегов один за другим и сокращаете набор запросов. Для каждого уровня вы повторно фильтруете весь набор запросов только для тех элементов, которые имеют все упомянутые теги.

так:

  • первый вызов / уровень будет для всех предметов, имеющих тег любимый ,
  • второй вызов / уровень будет для всех предметов, имеющих теги любимый и самый громкий ,
  • и т.д.

Если набор запросов, возвращаемый фильтром, равен None, это означает, что нет элементов, которые имеют все необходимые теги, и метод завершит работу и вернет None (т.е. он завершится при первом возможном случае сбоя). Кроме того, должен быть только один удар по базе данных (я думаю!)

Я проверил это, и оно должно работать, так что попробуйте

EDIT

Чтобы объединить набор запросов, возвращенный из брендов (q1), и набор запросов, созданный выше, с помощью itertools (q2):

list = []
for item in itertools.chain(q1, q2):
    list.append(item)

РЕДАКТИРОВАТЬ 2

Разве это не выполняет то, что вам нужно в одном запросе?

# list of tags = ['comedy','80s']
qs = Item.objects.all( Q(brand__iexact="brand name") | Q(tags__name__in=[tag for tag in list_of_tags]) )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...