111

I am trying to filter a bunch of objects through a many-to-many relation. Because the trigger_roles field may contain multiple entries I tried the contains filter. But as that is designed to be used with strings I'm pretty much helpless how i should filter this relation (you can ignore the values_list() atm.).

This function is attached to the user profile:

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)

My workflow model looks like this (simplified):

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)

Though the solution might be quiet simple, my brain won't tell me.

Thanks for your help.

djvg
  • 11,722
  • 5
  • 72
  • 103
Grave_Jumper
  • 1,224
  • 2
  • 8
  • 11

4 Answers4

135

Have you tried something like this:

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

or just if self.role.id is not a list of pks:

module.workflow_set.filter(trigger_roles__id__exact=self.role.id, allowed=True)
laffuste
  • 16,287
  • 8
  • 84
  • 91
mouad
  • 67,571
  • 18
  • 114
  • 106
  • 2
    That does not seem to work. As the self.role.id is just one int and the trigger_roles is a list of them, i'd need a inverted in, like contains but as i've found out, contains is only for strings. – Grave_Jumper Dec 22 '10 at 11:24
  • 9
    The second example *should* work. If the value in `self.role.id` is one of the trigger roles, then that filter should pull all workflows where one of the trigger roles is the value in `self.role.id`. Basically this will behave exactly like a "contains" function. Unless we're all missing something. – Jordan Reiter Dec 22 '10 at 13:36
  • @Jordan Reiter: "contains" is converted in sql to "like" which not what the OP want and i think he already point this out, in the other hand "exact" is converted to "=" or "is" which is the idea here. – mouad Dec 22 '10 at 14:07
  • @Grave_Jumper: Take a look here (http://www.djangoproject.com/documentation/models/many_to_many/) you can find some example when working with ManytoMany Field , hope this can help you, if my answer is not :) – mouad Dec 22 '10 at 14:08
  • 1
    aww.. sorry your second solution works quiet well :) There was a little miss configuration on my side. Thanks guys this saved my day ;-) – Grave_Jumper Dec 22 '10 at 14:27
  • While it is a little confusing `module.workflow_set.filter(trigger_roles=self)` is the go. – Paul Whipp May 14 '14 at 03:58
  • What does `allowed=True` do? – Sean Letendre Sep 29 '17 at 20:29
  • 1
    @SeanLetendre: It's one of the filters applied (see the `allowed` attribute on the `Workflow` model in the example). It means nothing out of context. – Sasha Chedygov Dec 05 '19 at 22:30
27

The simplest approach to achieve this would be checking for equalty over the whole instance (instead of the id) in the ManyToManyField. That looks if the instance is inside the many to many relationship. Example:

module.workflow_set.filter(trigger_roles=self.role, allowed=True)
Caumons
  • 9,341
  • 14
  • 68
  • 82
9

I know this is an old question, but it looks like the OP never quite got the answer he was looking for. If you have two sets of ManyToManyFields you want to compare, the trick is to use the __in operator, not contains. So for example if you have an "Event" model with a ManyToMany to "Group" on field eventgroups, and your User model (obviously) attaches to Group, you can query like this:

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

shacker
  • 14,712
  • 8
  • 89
  • 89
7

singularity is almost right with the first example. You just need to make sure it's a list. The second example, checking the trigger_roles__id__exact is a better solution though.

module.workflow_set.filter(trigger_roles__in=[self.role.id],allowed=True)
Josh Smeaton
  • 47,939
  • 24
  • 129
  • 164