Sto progettando un database abbastanza complesso e so che alcune delle mie query saranno molto al di fuori dell'ambito dell'ORM di Django. Qualcuno ha integrato gli SP con l'ORM di Django con successo? Se sì, quali RDBMS e come l'hai fatto?Qual è il modo migliore per accedere alle stored procedure nell'ORM di Django
risposta
Noi (musicpictures.com/eviscape.com) abbiamo scritto che django snippet ma non è l'intera storia (in realtà quel codice era stato testato solo su Oracle in quel momento).
Le stored procedure hanno senso quando si desidera riutilizzare il codice SP provato e testato o dove una chiamata SP sarà più veloce di più chiamate al database o laddove la sicurezza richiede un accesso moderato al database o dove le query sono molto complicate/multistep. Stiamo utilizzando un approccio modello/SP ibrido contro i database Oracle e Postgres.
Il trucco è renderlo facile da usare e tenerlo come "django". Usiamo una funzione make_instance che prende il risultato del cursore e crea istanze di un modello popolato dal cursore. Questo è bello perché il cursore potrebbe restituire campi aggiuntivi. Quindi è possibile utilizzare tali istanze nel codice/modelli in modo simile ai normali oggetti del modello django.
def make_instance(instance, values):
'''
Copied from eviscape.com
generates an instance for dict data coming from an sp
expects:
instance - empty instance of the model to generate
values - dictionary from a stored procedure with keys that are named like the
model's attributes
use like:
evis = InstanceGenerator(Evis(), evis_dict_from_SP)
>>> make_instance(Evis(), {'evi_id': '007', 'evi_subject': 'J. Bond, Architect'})
<Evis: J. Bond, Architect>
'''
attributes = filter(lambda x: not x.startswith('_'), instance.__dict__.keys())
for a in attributes:
try:
# field names from oracle sp are UPPER CASE
# we want to put PIC_ID in pic_id etc.
setattr(instance, a, values[a.upper()])
del values[a.upper()]
except:
pass
#add any values that are not in the model as well
for v in values.keys():
setattr(instance, v, values[v])
#print 'setting %s to %s' % (v, values[v])
return instance
# usare in questo modo:
pictures = [make_instance(Pictures(), item) for item in picture_dict]
# e qui ci sono alcune funzioni di aiuto:
def call_an_sp(self, var):
cursor = connection.cursor()
cursor.callproc("fn_sp_name", (var,))
return self.fn_generic(cursor)
def fn_generic(self, cursor):
msg = cursor.fetchone()[0]
cursor.execute('FETCH ALL IN "%s"' % msg)
thing = create_dict_from_cursor(cursor)
cursor.close()
return thing
def create_dict_from_cursor(cursor):
rows = cursor.fetchall()
# DEBUG settings (used to) affect what gets returned.
if DEBUG:
desc = [item[0] for item in cursor.cursor.description]
else:
desc = [item[0] for item in cursor.description]
return [dict(zip(desc, item)) for item in rows]
applausi, Simon.
Perché chiudi il cursore in 'fn_generic'? –
Lavoro su un sistema immenso con un database a cui si accede da più applicazioni, alcuni C++, alcuni python, alcuni perl, alcuni php, alcuni web sono basati, molti non lo sono. Mi piace quando la logica aziendale è in SP perché significa che la logica è coerente in tutte le implementazioni e, almeno nel nostro caso, rende la manutenzione molto più semplice. –
ho trovato questo commento da russ magee: "Abbiamo specificamente evitato di aggiungere funzionalità ovali simili a SQL a ORM di Django, perché alla fine della giornata, non stiamo provando a sostituire SQL - stiamo solo cercando di fornisce un modo conveniente per esprimere semplici query. Ci si aspetta che torni a chiamare solo SQL raw per casi complessi " –
è necessario utilizzare l'utilità di connessione in Django:
from django.db import connection
cursor = connection.cursor()
cursor.execute("SQL STATEMENT CAN BE ANYTHING")
allora è possibile recuperare i dati:
cursor.fetchone()
o:
cursor.fetchall()
Maggiori informazioni qui: http://docs.djangoproject.com/en/dev/topics/db/sql/
Se si desidera esaminare un progetto in esecuzione che utilizza SP, consultare minibooks. Una buona dose di SQL personalizzato e utilizza Postgres pl/pgsql per SP. Penso che alla fine rimuoveranno l'SP (giustificazione in trac ticket 92).
No.
Seriamente.
Spostare la logica della procedura memorizzata nel modello a cui appartiene.
Inserire un codice in Django e qualche codice nel database è un incubo di manutenzione. Ho trascorso troppi dei miei 30 anni in IT cercando di ripulire questo tipo di casino.
Quello che sta chiedendo è di integrare l'ORM con SP. Questo probabilmente non è possibile, e l'uso di stored procedure probabilmente richiede semplicemente di accedere a django.db.connection direttamente come nelle altre risposte, ma sarebbe interessante se fosse possibile spostare automaticamente le query comuni che l'ORM fa nelle stored procedure, per salvare sul tempo di generazione delle query, e farlo in modo trasparente, come ottimizzazione. Non funzionerebbe su tutti i database e probabilmente il guadagno di prestazioni non è utile, ma sarebbe divertente indagare. – Chad
@Chad: "Quello che sta chiedendo è di integrare l'ORM con SP". Inteso. Quindi la mia risposta. I frammenti di SP frammentano la logica applicativa tra il codice applicativo corretto e il database. Spesso creano più problemi di quanti ne risolvano. Penso che gli SP non siano utili in nessuna circostanza e non dovrebbero essere usati. –
@ S.Lott Penso che abbiate frainteso il punto che ho fatto. Sto parlando di un Django immaginario/futuro. Le stored procedure non verranno scritte dagli sviluppatori. Questo ORM convertirà in modo dinamico/trasparente le query ORM comunemente eseguite in stored procedure, in modo che possa risparmiare tempo sulla generazione di stringhe SQL e utilizzare la natura precompilata di SP. Ancora una volta, non sto affermando di pensare che sia nemmeno possibile, o che varrebbe la pena accelerare. Solo sottolineando un'idea interessante che la sua domanda ha generato per me. Questo approccio potrebbe lasciare tutta la logica del codice e avere prestazioni SP. – Chad
Immagino che il migliorato supporto per sql queryset raw in Django 1.2 possa semplificare la procedura in quanto non dovresti eseguire il rollover del tuo codice di tipo make_instance.
V'è un buon esempio: https://djangosnippets.org/snippets/118/
from django.db import connection
cursor = connection.cursor()
ret = cursor.callproc("MY_UTIL.LOG_MESSAGE", (control_in, message_in))# calls PROCEDURE named LOG_MESSAGE which resides in MY_UTIL Package
cursor.close()
[Django utilizzo di stored procedure] (http://www.djangosnippets.org/snippets/118) darà qualche idea. – Dhana