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.