Django filtruje wiele do wielu z zawartością

90

Próbuję przefiltrować zbiór obiektów przez relację wiele do wielu. Ponieważ trigger_rolespole może zawierać wiele wpisów, wypróbowałem containsfiltr. Ale ponieważ jest to przeznaczone do użytku ze stringami, jestem prawie bezradny, jak powinienem filtrować tę relację (możesz zignorować values_list()atm.).

Ta funkcja jest dołączona do profilu użytkownika:

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)

Mój model przepływu pracy wygląda następująco (uproszczony):

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)

Chociaż rozwiązanie może być dość proste, mój mózg mi nie powie.

Dzięki za pomoc.

Grave_Jumper
źródło

Odpowiedzi:

110

Czy próbowałeś czegoś takiego:

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

lub po prostu jeśli self.role.idnie jest to lista pks:

module.workflow_set.filter(trigger_roles__id__exact=self.role.id, allowed=True)
mouad
źródło
1
To nie wydaje się działać. Ponieważ self.role.id to tylko jeden int, a trigger_roles to ich lista, potrzebowałbym inwersji, na przykład zawiera, ale jak się dowiedziałem, zawiera tylko dla ciągów.
Grave_Jumper,
8
Drugi przykład powinien zadziałać. Jeśli wartość w self.role.idjest jedną z ról wyzwalacza, ten filtr powinien pobierać wszystkie przepływy pracy, w których jedna z ról wyzwalacza jest wartością w self.role.id. Zasadniczo będzie to zachowywać się dokładnie tak, jak funkcja „zawiera”. Chyba że wszyscy czegoś brakuje.
Jordan Reiter
@Jordan Reiter: „zawiera” jest konwertowane w sql na „podobne”, co nie jest tym, czego chce OP i myślę, że już na to wskazuje, z drugiej strony „dokładne” jest konwertowane na „=” lub „jest”, co jest pomysł tutaj.
mouad
@Grave_Jumper: Spójrz tutaj ( djangoproject.com/documentation/models/many_to_many ) możesz znaleźć przykład podczas pracy z ManytoMany Field, mam nadzieję, że to ci pomoże, jeśli moja odpowiedź nie brzmi :)
mouad
1
aww .. przepraszam, że drugie rozwiązanie działa dobrze :) Po mojej stronie była mała brakująca konfiguracja. Dzięki chłopaki, to uratowało mi dzień ;-)
Grave_Jumper,
19

Najprostszym podejściem do osiągnięcia tego byłoby sprawdzenie równości w całej instancji (zamiast id) w pliku ManyToManyField. Wygląda na to, że instancja znajduje się w relacji wiele do wielu. Przykład:

module.workflow_set.filter(trigger_roles=self.role, allowed=True)
Caumons
źródło
7

Wiem, że to stare pytanie, ale wygląda na to, że OP nigdy nie uzyskał odpowiedzi, której szukał. Jeśli masz dwa zestawy ManyToManyFields, które chcesz porównać, sztuczka polega na użyciu __inoperatora, a nie contains. Na przykład, jeśli masz model „Event” z polem ManyToMany do „Group” eventgroups, a model użytkownika (oczywiście) jest dołączony do grupy, możesz zapytać w ten sposób:

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

shacker
źródło
4

osobliwość ma prawie rację w pierwszym przykładzie. Musisz tylko upewnić się, że to lista. Drugi przykład, sprawdzenie trigger_roles__id__exactjest lepszym rozwiązaniem.

module.workflow_set.filter(trigger_roles__in=[self.role.id],allowed=True)
Josh Smeaton
źródło