Um quebra-cabeça sobre Q objetos e chaves estrangeiras
-
06-07-2019 - |
Pergunta
Eu tenho um modelo como este:
class Thing(models.Model):
property1 = models.IntegerField()
property2 = models.IntegerField()
property3 = models.IntegerField()
class Subthing(models.Model):
subproperty = models.IntegerField()
thing = modelsForeignkey(Thing)
main = models.BooleanField()
Eu tenho uma função que é passada uma lista de filtros, onde cada filtro é da forma { 'type': algo, 'valor': x}. Esta função deve retornar um conjunto de resultados Anding todos os filtros juntos:
final_q = Q()
for filter in filters:
q = None
if filter['type'] =='thing-property1':
q = Q(property1=filter['value'])
elif filter['type'] =='thing-property2':
q = Q(property2=filter['value'])
elif filter['type'] =='thing-property2':
q = Q(property3=filter['value'])
if q:
final_q = final_q & q
return Thing.objects.filter(final_q).distinct()
Cada Subthing tem uma propriedade booleana 'main'. Cada coisa tem 1 e apenas 1 Subthing onde principal == True.
Agora eu preciso adicionar filtro que retorna todas as coisas que têm um Subthing onde main==True
e subproperty==filter['value']
Posso fazer isso como parte do objeto Q
Estou construindo? Se não como pessoa? O queryset eu chegar antes do meu novo filtro pode ser bastante grande, então eu gostaria de um método que não envolve looping sobre os resultados.
Solução
É um pouco mais fácil de entender se você dá explicitamente seus Subthings um "related_name" em sua relação com a coisa
class Subthing(models.Model):
...
thing = models.ForeignKey(Thing, related_name='subthings')
...
Agora, você usa Django juntar sintaxe para construir o seu objeto Q:
Q(subthings__main=True) & Q(subthings__subproperty=filter['value'])
A relação inversa tem o nome default 'subthing_set', mas eu acho que é mais fácil de seguir, se você dar-lhe um nome melhor como 'subthings'.
Outras dicas
Usando (em vez de final_q=Q()
no início)
final_q=Q(subthing_set__main=True)
sub_vals = map(lambda v: v['value'], filters)
if sub_vals:
final_q = final_q & Q(subthing_set__subproperty__in=sub_vals)
deve conseguir o que você quiser, você também pode ajustar o loop para construir a lista sub_vals e aplicá-lo após o loop.
subthing_set é e adicionados automaticamente campo relacionado adicionado à Coisa de acesso relacionados Subthings.
Você pode atribuir outro nome relacionado, por exemplo.
thing=models.ForeignKey(Thing,related_name='subthings')