Django filtra muitos para muitos com contém

87

Estou tentando filtrar um monte de objetos por meio de uma relação muitos para muitos. Como o trigger_rolescampo pode conter várias entradas, experimentei o containsfiltro. Mas, como isso foi projetado para ser usado com cordas, não tenho como saber como filtrar essa relação (você pode ignorar o values_list()atm.).

Esta função está anexada ao perfil do usuário:

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)

Meu modelo de fluxo de trabalho é assim (simplificado):

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)

Embora a solução possa ser bem simples, meu cérebro não me diz.

Obrigado pela ajuda.

Grave_Jumper
fonte

Respostas:

109

Você já tentou algo assim:

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

ou apenas se self.role.idnão for uma lista de pks:

module.workflow_set.filter(trigger_roles__id__exact=self.role.id, allowed=True)
muad
fonte
1
Isso não parece funcionar. Como self.role.id é apenas um int e trigger_roles é uma lista deles, eu precisaria de um in invertido, como contém, mas como descobri, contém é apenas para strings.
Grave_Jumper
8
O segundo exemplo deve funcionar. Se o valor em self.role.idfor uma das funções do acionador, esse filtro deve puxar todos os fluxos de trabalho em que uma das funções do acionador é o valor em self.role.id. Basicamente, isso se comportará exatamente como uma função "contém". A menos que todos estejamos perdendo alguma coisa.
Jordan Reiter
@Jordan Reiter: "contém" é convertido em sql para "como" que não é o que o OP quer e acho que ele já apontou isso, por outro lado "exato" é convertido para "=" ou "é" que é o ideia aqui.
mouad
@Grave_Jumper: Dê uma olhada aqui ( djangoproject.com/documentation/models/many_to_many ) você pode encontrar alguns exemplos ao trabalhar com o campo ManytoMany, espero que isso possa ajudá-lo, se minha resposta não for :)
mouad
1
aww .. desculpe, sua segunda solução funciona silenciosamente bem :) Houve um pequeno erro de configuração do meu lado. Obrigado pessoal, isso salvou meu dia ;-)
Grave_Jumper
18

A abordagem mais simples para conseguir isso seria verificar a igualdade em toda a instância (em vez do id) no ManyToManyField. Parece que a instância está dentro do relacionamento muitos para muitos. Exemplo:

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

Sei que essa é uma pergunta antiga, mas parece que o OP nunca obteve a resposta que estava procurando. Se você tiver dois conjuntos de ManyToManyFields que deseja comparar, o truque é usar o __inoperador, não contains. Então, por exemplo, se você tiver um modelo de "Evento" com um ManyToMany para "Grupo" no campo eventgroups, e seu modelo de Usuário (obviamente) anexar ao Grupo, você pode consultar desta forma:

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

shacker
fonte
4

a singularidade está quase certa com o primeiro exemplo. Você só precisa ter certeza de que é uma lista. O segundo exemplo, verificando o, trigger_roles__id__exacté uma solução melhor.

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