Django фильтрует многие ко многим с помощью contains

87

Я пытаюсь отфильтровать кучу объектов через отношение «многие ко многим». Поскольку trigger_rolesполе может содержать несколько записей, я попробовал containsприменить фильтр. Но поскольку это предназначено для использования со строками, я почти беспомощен, как мне фильтровать это отношение (вы можете игнорировать values_list()банкомат).

Эта функция привязана к профилю пользователя:

def getVisiblePackages(self):
    visiblePackages = {}   
    for product in self.products.all():
        moduleDict = {}
        for module in product.module_set.all():
            pkgList = []
            involvedStatus = module.workflow_set.filter(trigger_roles__contains=self.role.id,allowed=True).values_list('current_state', flat=True)

Моя модель рабочего процесса выглядит так (упрощенно):

class Workflow(models.Model):
    module = models.ForeignKey(Module)
    current_state = models.ForeignKey(Status)
    next_state = models.ForeignKey(Status)
    allowed = models.BooleanField(default=False)
    involved_roles = models.ManyToManyField(Role, blank=True, null=True)
    trigger_roles = models.ManyToManyField(Role, blank=True, null=True)

Хотя решение может быть довольно простым, мой мозг не подскажет.

Спасибо за вашу помощь.

Grave_Jumper
источник

Ответы:

109

Вы пробовали что-то подобное:

module.workflow_set.filter(trigger_roles__in=[self.role], allowed=True)

или просто если self.role.idэто не список ПК:

module.workflow_set.filter(trigger_roles__id__exact=self.role.id, allowed=True)
Муад
источник
1
Похоже, это не работает. Поскольку self.role.id - это всего лишь один int, а trigger_roles - это их список, мне понадобится инвертированный in, например, contains, но, как я узнал, contains только для строк.
Grave_Jumper
8
Второй пример должен работать. Если значение в self.role.id- одна из ролей триггера, то этот фильтр должен отобрать все рабочие процессы, в которых одна из ролей триггера является значением в self.role.id. В основном это будет вести себя точно так же, как функция "содержит". Если только мы все чего-то не упустим.
Jordan Reiter
@Jordan Reiter: "contains" преобразуется в sql в "like", что не то, что хочет OP, и я думаю, что он уже указал на это, с другой стороны, "точное" преобразовано в "=" или "is", что является идея здесь.
mouad
@Grave_Jumper: взгляните здесь ( djangoproject.com/documentation/models/many_to_many ), вы можете найти пример работы с ManytoMany Field, надеюсь, это поможет вам, если мой ответ нет :)
mouad
1
Оу .. извините, ваше второе решение работает хорошо :) На моей стороне была небольшая ошибка конфигурации. Спасибо, ребята, это спасло мне день ;-)
Grave_Jumper
18

Самый простой способ добиться этого - проверить равенство всего экземпляра (вместо идентификатора) в ManyToManyField. Это выглядит, если экземпляр находится внутри отношения многие ко многим. Пример:

module.workflow_set.filter(trigger_roles=self.role, allowed=True)
Caumons
источник
6

Я знаю, что это старый вопрос, но похоже, что ОП так и не получил ответа, который искал. Если у вас есть два набора ManyToManyFields, которые вы хотите сравнить, хитрость заключается в использовании __inоператора, а не contains. Так, например, если у вас есть модель «Событие» с полем ManyToMany to «Group» on eventgroups, а ваша модель User (очевидно) присоединена к Group, вы можете запросить следующее:

Event.objects.filter(eventgroups__in=u.groups.all())

шейкер
источник
4

сингулярность почти совпадает с первым примером. Вам просто нужно убедиться, что это список. Второй пример, проверка trigger_roles__id__exact- лучшее решение.

module.workflow_set.filter(trigger_roles__in=[self.role.id],allowed=True)
Джош Смитон
источник