00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Query Raw SQL

Sebbene l’ORM di Django copra la maggior parte delle esigenze, a volte e necessario eseguire query SQL dirette. Django offre diversi strumenti per farlo in modo sicuro e controllato.

Quando Usare Query Raw

Le query SQL dirette sono utili in scenari specifici:

  • Query con ottimizzazioni SQL specifiche del database
  • Funzioni SQL non supportate dall’ORM
  • Query molto complesse dove l’ORM genera SQL inefficiente
  • Integrazione con tabelle non gestite da Django

Tuttavia, preferisci sempre l’ORM quando possibile: e piu sicuro, portabile e manutenibile.

Il Metodo raw()

Il metodo raw() esegue una query SQL e restituisce istanze del modello:

from blog.models import Articolo

# Query raw base
articoli = Articolo.objects.raw('SELECT * FROM blog_articolo')
for articolo in articoli:
    print(articolo.titolo)  # accedi ai campi come un normale oggetto

# Con parametri (SICURO contro SQL injection)
articoli = Articolo.objects.raw(
    'SELECT * FROM blog_articolo WHERE visualizzazioni > %s',
    [100]
)

# Con parametri nominati
articoli = Articolo.objects.raw(
    'SELECT * FROM blog_articolo WHERE autore_id = %(autore_id)s',
    {'autore_id': 5}
)

Il risultato di raw() e un RawQuerySet iterabile che restituisce istanze del modello con i campi popolati dalla query SQL.

Query con Campi Personalizzati

La query SQL puo includere campi calcolati che non esistono nel modello:

articoli = Articolo.objects.raw('''
    SELECT a.*, COUNT(c.id) AS num_commenti
    FROM blog_articolo a
    LEFT JOIN blog_commento c ON c.articolo_id = a.id
    GROUP BY a.id
    ORDER BY num_commenti DESC
''')

for articolo in articoli:
    print(f'{articolo.titolo}: {articolo.num_commenti}')

I campi extra sono accessibili come attributi dell’oggetto.

Usare connection.cursor()

Per query che non corrispondono a nessun modello, usa connection.cursor():

from django.db import connection

def statistiche_globali():
    with connection.cursor() as cursor:
        cursor.execute('''
            SELECT
                COUNT(*) AS totale,
                AVG(visualizzazioni) AS media_views
            FROM blog_articolo
            WHERE pubblicato = %s
        ''', [True])
        riga = cursor.fetchone()
        return {
            'totale': riga[0],
            'media_views': riga[1],
        }
# Ottenere tutte le righe
def elenco_autori():
    with connection.cursor() as cursor:
        cursor.execute('''
            SELECT autore_id, COUNT(*) as num_articoli
            FROM blog_articolo
            GROUP BY autore_id
            HAVING COUNT(*) > %s
        ''', [5])
        colonne = [col[0] for col in cursor.description]
        risultati = [
            dict(zip(colonne, riga))
            for riga in cursor.fetchall()
        ]
        return risultati

Funzioni Helper per il Cursor

Django fornisce metodi utili per leggere i risultati:

from django.db import connection

def dizionari_da_cursor(cursor):
    """Converte le righe del cursor in dizionari."""
    colonne = [col[0] for col in cursor.description]
    return [
        dict(zip(colonne, riga))
        for riga in cursor.fetchall()
    ]

with connection.cursor() as cursor:
    cursor.execute('SELECT id, titolo FROM blog_articolo LIMIT 10')
    risultati = dizionari_da_cursor(cursor)
    # [{'id': 1, 'titolo': 'Primo Articolo'}, ...]

Prevenzione SQL Injection

Non inserire mai valori direttamente nella stringa SQL. Usa sempre i segnaposto:

# SBAGLIATO - vulnerabile a SQL injection
cursor.execute(f"SELECT * FROM blog_articolo WHERE titolo = '{titolo}'")

# CORRETTO - usa segnaposto parametrizzati
cursor.execute("SELECT * FROM blog_articolo WHERE titolo = %s", [titolo])

# SBAGLIATO con raw()
Articolo.objects.raw(f"SELECT * FROM blog_articolo WHERE id = {user_input}")

# CORRETTO con raw()
Articolo.objects.raw("SELECT * FROM blog_articolo WHERE id = %s", [user_input])

I segnaposto %s vengono gestiti dal driver del database, che si occupa dell’escaping corretto dei valori.

RawSQL nelle Annotazioni

Per aggiungere espressioni SQL personalizzate all’interno di query ORM:

from django.db.models.expressions import RawSQL

articoli = Articolo.objects.annotate(
    lunghezza_titolo=RawSQL("LENGTH(titolo)", [])
).order_by('-lunghezza_titolo')

# Con parametri
articoli = Articolo.objects.annotate(
    recente=RawSQL(
        "data_pubblicazione > %s",
        ['2026-01-01'],
        output_field=models.BooleanField(),
    )
)

Database Multipli

Quando usi piu database, specifica quale usare:

from django.db import connections

with connections['analytics'].cursor() as cursor:
    cursor.execute('SELECT * FROM report_vendite')
    risultati = cursor.fetchall()

Conclusione

Le query raw SQL in Django sono uno strumento potente per scenari dove l’ORM non basta. Usa raw() quando il risultato corrisponde a un modello, connection.cursor() per query generiche e RawSQL per integrare frammenti SQL nell’ORM. La regola fondamentale e di usare sempre segnaposto parametrizzati per prevenire SQL injection e di preferire l’ORM quando possibile per garantire portabilita e sicurezza.