Artigos

Como combinar QuerySets no Django

O ORM é muito poderoso. Hoje vamos explorar os QuerySets e, mais especificamente, como combiná-los ou mesclá-los no Django.

QuerySet ao estilo Django

A documentação do Django é bastante clara sobre a API de QuerySet. Ela explica:

Internamente, um QuerySet pode ser construído, filtrado, fatiado e, em geral, passado de um lugar a outro sem realmente acessar o banco de dados. Nenhuma atividade ocorre no banco de dados até que você faça algo para avaliar o queryset. 1

Em outras palavras, recomenda-se fazer consultas e manipular dados com QuerySets em vez de consultas diretas. Estas são as razões:

Desempenho

Acessar o banco de dados apenas no momento da avaliação permite realizar muitas operações, como fatiamento e filtragem. Isso aumenta a flexibilidade e o desempenho da possível consulta, ao contrário das consultas diretas, que acessam o banco de dados a cada chamada.

Conveniência

É mais fácil filtrar, fatiar, iterar e manipular dados por meio de QuerySet porque você pode gerar uma lista de QuerySets que oferecem vários métodos para facilitar a manipulação. Já com consultas diretas, você precisa lidar com uma estrutura muito rígida que exige trabalho extra para entregar o mesmo resultado.

Segurança

Usar consultas diretas exige cautela, pois sempre é preciso evitar parâmetros controlados pelo usuário para proteger a consulta contra injeção de SQL.

Às vezes, precisamos combinar os resultados de vários QuerySets em um só para agrupá-los e iterar sobre eles em uma lista. Há duas situações possíveis:

Se o QuerySet trabalha com o mesmo modelo

Recomenda-se usar pipes e o operador | para realizar a mesclagem.

Considere os seguintes modelos:

class Group(models.Model):

   code = models.CharField(max_length=80, unique=True)
   name = models.CharField(max_length=128)
   users = models.ManyToManyField(User, related_name='groups')
   roles = models.ManyToManyField(Role, related_name='groups', blank=True)
   products = models.ManyToManyField(Product, related_name='groups', blank=True)


class Client(models.Model):

   name = models.CharField(max_length=30, unique=True)
   groups = models.ManyToManyField(Group)

Suponha que você queira exibir todos os grupos de um cliente específico junto com o grupo que pertence a um usuário, usando o nome de produto CMS A. Observe que esse User pode ter diferentes grupos.

client = Client.objects.get(name='Django Client Name')
user = User.objects.get(username='cdiaz')
django_groups = client.groups.all()
cdiaz_groups = user.client.filter(products__name='CMS A')

Agora temos dois QuerySets diferentes: um contém todos os grupos do cliente e o outro todos os grupos do usuário com o nome do produto.

groups = django_groups | cdiaz_groups  # merge querysets

Você pode examinar a consulta SQL gerada pelo Django usando o atributo query. O comando completo para o caso de groups é print(groups.query). O Django combina automaticamente as duas consultas, django_groups e cdiaz_groups, em uma única consulta SQL.

O processo é muito simples, mas considere que só funciona com QuerySets do mesmo modelo e antes do fatiamento.

Se os QuerySets trabalham com modelos diferentes

Suponha que temos três modelos diferentes, Post, Article e Page, que queremos combinar em um só para realizar operações como filtragem e ordenação. Uma abordagem seria usar chain de itertools:

from itertools import chain

def get_all_documents():
   articles = Article.objects.all()
   posts = Post.objects.all()
   pages = Page.objects.all()
   return list(chain(pages, articles, posts))

Agora podemos ordenar a lista resultante, por exemplo por data, usando a função sorted() e fazendo alguns pequenos ajustes.

from itertools import chain

def get_all_documents_sorted():
   articles = Article.objects.all()
   posts = Post.objects.all()
   pages = Page.objects.all()
   result_list = sorted(
       chain(pages, articles, posts),
       key=attrgetter('date_created'), reverse=True)
   return result_list

Dessa forma, a lista pode ser obtida em ordem inversa. É importante observar que o Django executa uma consulta SQL diferente para cada um dos três conjuntos nesse caso, mas elas não são executadas até que a função sorted itere sobre cada um.


Em resumo, mostramos várias abordagens para mesclar QuerySets com o mesmo modelo ou com modelos diferentes no Django sem depender de consultas diretas, permitindo aproveitar suas vantagens de desempenho, flexibilidade e conveniência.


  1. "Referência da API de QuerySet," Documentação, Django Project. ↩

“Como combinar QuerySets no Django” por Cesar Diaz está sob a licença CC BY SA. Os exemplos de código-fonte estão sob a licença MIT.

Foto de Kelly Sikkema.

Classificado em Pesquisa e aprendizado.

Leituras relacionadas