Django filtre plusieurs-à-plusieurs avec contient

87

J'essaye de filtrer un groupe d'objets à travers une relation plusieurs-à-plusieurs. Parce que le trigger_roleschamp peut contenir plusieurs entrées, j'ai essayé le containsfiltre. Mais comme cela est conçu pour être utilisé avec des chaînes, je suis pratiquement incapable de filtrer cette relation (vous pouvez ignorer l' values_list()atm.).

Cette fonction est attachée au profil utilisateur:

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)

Mon modèle de workflow ressemble à ceci (simplifié):

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)

Bien que la solution soit assez simple, mon cerveau ne me le dira pas.

Merci de votre aide.

Grave_Jumper
la source

Réponses:

109

Avez-vous essayé quelque chose comme ceci:

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

ou juste si ce self.role.idn'est pas une liste de pks:

module.workflow_set.filter(trigger_roles__id__exact=self.role.id, allowed=True)
mouad
la source
1
Cela ne semble pas fonctionner. Comme self.role.id n'est qu'un int et que trigger_roles est une liste d'entre eux, j'aurais besoin d'un inversé in, comme contient mais comme je l'ai découvert, contient n'est que pour les chaînes.
Grave_Jumper
8
Le deuxième exemple devrait fonctionner. Si la valeur dans self.role.idest l'un des rôles de déclencheur, ce filtre doit extraire tous les flux de travail où l'un des rôles de déclencheur est la valeur dans self.role.id. Fondamentalement, cela se comportera exactement comme une fonction "contient". À moins que nous manquions tous quelque chose.
Jordan Reiter
@Jordan Reiter: "contient" est converti en sql en "comme", ce qui n'est pas ce que veut l'OP et je pense qu'il le souligne déjà, en revanche "exact" est converti en "=" ou "est" qui est le idée ici.
mouad
@Grave_Jumper: Jetez un œil ici ( djangoproject.com/documentation/models/many_to_many ) vous pouvez trouver un exemple lorsque vous travaillez avec ManytoMany Field, j'espère que cela pourra vous aider, si ma réponse n'est pas :)
mouad
1
aww .. désolé ta deuxième solution fonctionne bien tranquillement :) Il y a eu un petit manque de configuration de mon côté. Merci les gars, cela m'a sauvé la journée ;-)
Grave_Jumper
18

L'approche la plus simple pour y parvenir serait de vérifier l'équité sur toute l'instance (au lieu de l'id) dans le ManyToManyField. Cela regarde si l'instance est à l'intérieur de la relation plusieurs à plusieurs. Exemple:

module.workflow_set.filter(trigger_roles=self.role, allowed=True)
Caumons
la source
6

Je sais que c'est une vieille question, mais il semble que le PO n'ait jamais tout à fait obtenu la réponse qu'il recherchait. Si vous souhaitez comparer deux ensembles de ManyToManyFields, l'astuce consiste à utiliser l' __inopérateur, non contains. Ainsi, par exemple, si vous avez un modèle "Event" avec un champ ManyToMany à "Group" sur eventgroups, et que votre modèle User (évidemment) s'attache à Group, vous pouvez interroger comme ceci:

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

shacker
la source
4

la singularité a presque raison avec le premier exemple. Vous devez juste vous assurer que c'est une liste. Le deuxième exemple, vérifier le trigger_roles__id__exactest une meilleure solution cependant.

module.workflow_set.filter(trigger_roles__in=[self.role.id],allowed=True)
Josh Smeaton
la source