Consultas SQL cruas em mssql-django

Este artigo explica como executar consultas SQL brutas contra o SQL Server a partir de aplicações Django. O SQL bruto é útil para operações não expostas através do ORM Django, como Transact-SQL complexo (T-SQL), consultas espaciais ou operações críticas de desempenho.

Utilize connection.cursor()

Acede diretamente ao cursor da base de dados através do connection objeto de Django:

from django.db import connection

def get_server_version():
    with connection.cursor() as cursor:
        cursor.execute("SELECT @@VERSION;")
        row = cursor.fetchone()
    return row[0]

A with instrução garante que o cursor está devidamente fechado após a utilização.

Consultas parametrizadas

Use sempre consultas parametrizadas para evitar a injeção de SQL. Passe os parâmetros como uma lista:

Note

Os exemplos seguintes utilizam a convenção <app_label>_<model_name> padrão de nomeação de tabelas do Django (por exemplo, myapp_product). Se sobrescreveres db_table no Meta de um modelo, substitui esse nome. Também pode ler o nome resolvido em tempo de execução com Product._meta.db_table.

from django.db import connection

def get_products_by_price(min_price, max_price):
    with connection.cursor() as cursor:
        cursor.execute(
            "SELECT id, name, price FROM myapp_product WHERE price BETWEEN %s AND %s;",
            [min_price, max_price],
        )
        results = cursor.fetchall()
    return results

Importante

Nunca uses formatação de strings ou f-strings para incorporar valores em consultas SQL. Use sempre consultas parametrizadas (%s marcadores com uma lista de parâmetros) para evitar a injeção de SQL.

Obter resultados

O cursor de Django fornece vários métodos para recuperar resultados:

from django.db import connection

def demonstrate_fetch_methods():
    with connection.cursor() as cursor:
        cursor.execute("SELECT id, name FROM myapp_product;")

        # Fetch one row
        row = cursor.fetchone()

        # Fetch the next 10 rows (continues from where fetchone stopped)
        rows = cursor.fetchmany(10)

        # Fetch all remaining rows (continues from where fetchmany stopped)
        all_rows = cursor.fetchall()

Devolver resultados como dicionários

Converter linhas para dicionários usando cursor.description:

from django.db import connection

def dictfetchall(cursor):
    columns = [col[0] for col in cursor.description]
    return [dict(zip(columns, row)) for row in cursor.fetchall()]

def get_all_products():
    with connection.cursor() as cursor:
        cursor.execute("SELECT id, name, price FROM myapp_product;")
        return dictfetchall(cursor)

Utilize Manager.raw() para consultas ao modelo

Quando quiseres SQL puro mas ainda assim queres instâncias do modelo Django, usa Manager.raw():

from myapp.models import Product

products = Product.objects.raw(
    "SELECT id, name, price FROM myapp_product WHERE price > %s",
    [10.00],
)

for product in products:
    print(f"{product.name}: ${product.price}")

A consulta deve devolver todos os campos definidos na chave primária do modelo. Campos adicionais são carregados preguiçosamente.

Executar instruções DDL

Use SQL bruto para operações de esquema que o Django não suporta diretamente:

from django.db import connection

def create_index():
    with connection.cursor() as cursor:
        cursor.execute(
            "CREATE INDEX IX_product_name ON myapp_product (name) "
            "INCLUDE (price);"
        )

Múltiplas ligações a bases de dados

Se usar várias bases de dados, especifique qual a ligação a usar:

from django.db import connections

def query_reporting_db():
    with connections["reporting"].cursor() as cursor:
        cursor.execute("SELECT COUNT(*) FROM myapp_product;")
        return cursor.fetchone()[0]