Django - Composição de queries brutas (raw queries)

Django Rest Framework (DRF) é uma poderosa ferramenta para criar Web APIs. Ele requer o uso de Python e do framework Django. Mesmo o Django já trazendo seu próprio ORM (Object Relational Mapping), o que facilita enormemente a construção de consultas SQL, em algum momento da aplicação pode ser necessário lançar mão de consultas brutas.

O DRF oferece duas maneiras de executar consultas cruas: a primeira utiliza o método raw() da classe Manager e a segunda faz acesso direto ao banco de dados. Independente da maneira que se adote, não se pode perder de vista a preocupação com a segurança no que diz respeito à injeção de SQL.

Supondo que já temos uma aplicação Django criada e rodando e que temos um model denominado Client, este código é um exemplo do que seria a primeira maneira:

client_id = 101
clients = Client.objects.raw(
  "SELECT * FROM client WHERE id = %s",
  [client_id]
)
for client in clients:
  print(client.name)
Caso optemos pelo acesso direto ao banco de dados, podemos fazer assim:
from django.db import connection

client_id = 101
with connection.cursor() as cursor:
  cursor.execute(
	"SELECT * FROM client WHERE id = %s",
  	[client_id]
  )
  client.cursor.fetchone()
  print(client[1])
Essas soluções se aplicam quando os parâmetros são valores mesmo no comando SQL. Consideremos o caso em que seja necessário especificar dinamicamente nomes de tabelas, campos ou nomes de schema. Neste cenário é útil o pacote psycopg2, levando em conta que a API acessa um banco de dados PostgreSQL.
Para usar este pacote, o primeiro passo consiste em instalá-lo, por exemplo, utilizando pip install psycopg2.
Deste pacote, será utilizado o módulo sql, para a composição de string SQL de forma segura, e duas classes do módulo: SQL, a qual representa um comando SQL, e Identifier, a qual representa um identificar PostgreSQL ou uma sequência de identificadores separados por ponto.
Aqui está um trecho de código que exemplifica o uso de psycopg2 para compor um comando SQL:
from django.db import connection
from psycopg2 import sql

client_id = 101
with connection.cursor() as cursor:
  cursor.execute(
    sql.SQL("SELECT * FROM {table_name} WHERE id = %s").format(
      table_name = sql.Identifier("schema", "client")
    ),
    [client_id]
  )
  client.cursor.fetchone()
  print(client[1])
A classe Identifier se aplica a um marcador {} na string SQL, neste exemplo {table_name}. Pode-se passar um ou mais parâmetros em Identifier. Caso sejam mais de um, eles aparecerão separados por ponto na query composta. Aqui estamos passando o nome do schema e o nome da tabela. Desse modo, após a composição a query ficará:
SELECT * FROM "schema"."client" WHERE id = %s
Como dissemos anteriormente, de modo análogo, é possível especificar nomes de campos.

Mais detalhes podem ser obtidos na documentação do Django.

Nenhum comentário:

Django - Composição de queries brutas (raw queries)

Django Rest Framework (DRF) é uma poderosa ferramenta para criar Web APIs. Ele requer o uso de Python e do framework Django. Mesmo o Django...