Django объединяет запросы AND и OR с полем ManyToMany

Надеясь, что кто-то может помочь мне с этим.

Я пытаюсь выяснить, могу ли я создать запрос, который позволит мне получать элементы из моей базы данных на основе поля ForeignKey и ManyToManyField одновременно. Сложной частью является то, что ему нужно будет фильтровать несколько объектов ManyToMany.

Надеюсь, пример прояснит это. Вот мои (упрощенные) модели:

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. Элементы, загруженные пользователями, за которыми следит пользователь (в модели это называется "бренд"). Так, например, если пользователь следует учетной записи Paramount, я бы хотел, чтобы все элементы, где brand = Paramount.

  2. Элементы, соответствующие ключевым словам в сохраненных поисках. Например, пользователь может сделать и сохранить следующий поиск: "комедия 80-х". В этом случае я хотел бы, чтобы все элементы, в которых теги содержали и "80-е", и "комедию".

Теперь я знаю, как построить запрос для каждого независимо. Для #1 это:

items = Item.objects.filter(brand=brand)

И для № 2 (на основе документов):

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

Мой вопрос: возможно ли сконструировать это как один запрос, чтобы мне не приходилось преобразовывать каждый запрос в список, объединять их и удалять дубликаты?

Похоже, проблема в том, что нет способа использовать объекты Q для создания запросов, где вам нужно много-многозначное поле элемента (в данном случае теги), чтобы соответствовать нескольким значениям. Следующий запрос:

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

не работает.

(См.: https://docs.djangoproject.com/en/dev/topics/db/queries/)

Заранее спасибо за вашу помощь в этом!

2 ответа

Решение

После долгих исследований я не смог найти способ объединить это в один запрос, поэтому я закончил преобразование моих QuerySets в списки и их объединение.

Фильтры Джанго автоматически AND. Q объекты нужны только если вы пытаетесь добавить OR. Так же __in Фильтр запросов поможет вам много.

Предполагая, что у пользователей есть несколько брендов, которые им нравятся, и вы хотите вернуть любой бренд, который им нравится, вы должны использовать:

`brand__in=brands`

куда brands это набор запросов, возвращаемый чем-то вроде someuser.brands.all(),

То же самое можно использовать для параметров поиска:

`tags__name__in=['80s','comedy']`

Это вернет вещи с тегами "80-х" или "комедия". Если вам нужны оба (вещи, отмеченные как "80-е", так и "комедия"), вам придется передать каждый из них в последовательный фильтр:

keywords = ['80s','comedy']
for keyword in keywords:
    qs = qs.filter(tags__name=keyword)

PS related_name значения всегда должны указывать противоположные отношения. У вас будут логические проблемы с тем, как вы это делаете в настоящее время. Например:

brand = models.ForeignKey(User, related_name='brand')

Значит это somebrand.brand.all() на самом деле вернется Item объекты. Так должно быть:

brand = models.ForeignKey(User, related_name='items')

Затем вы можете получить товары бренда с somebrand.items.all(), Имеет гораздо больше смысла.

Другие вопросы по тегам