Qual è il modo migliore per accedere alle procedure memorizzate nell'ORM di Django
-
03-07-2019 - |
Domanda
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 con successo gli SP con l'ORM di Django? In tal caso, quale RDBMS e come hai fatto?
Soluzione
Noi (musicpictures.com / eviscape.com) abbiamo scritto quel frammento di django ma non è l'intera storia (in realtà quel codice è stato testato solo su Oracle in quel momento).
Le procedure memorizzate 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 dove la sicurezza richiede un accesso moderato al database - o dove le query sono molto complicate / fasi. Stiamo usando un modello ibrido / approccio SP contro database Oracle e Postgres.
Il trucco è renderlo facile da usare e tenerlo " django " piace. Usiamo una funzione make_instance che prende il risultato del cursore e crea istanze di un modello popolato dal cursore. Questo è utile perché il cursore potrebbe restituire campi aggiuntivi. Quindi puoi usare quelle istanze nel tuo codice / template in modo simile ai normali oggetti 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
# Usalo in questo modo:
pictures = [make_instance(Pictures(), item) for item in picture_dict]
# Ed ecco 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]
evviva, Simon.
Altri suggerimenti
Devi usare l'utilità di connessione in Django:
from django.db import connection
cursor = connection.cursor()
cursor.execute("SQL STATEMENT CAN BE ANYTHING")
quindi puoi recuperare i dati:
cursor.fetchone()
o
cursor.fetchall()
Maggiori informazioni qui: http://docs.djangoproject.com/en / dev / argomenti / db / sql /
C'è 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()
Se vuoi guardare un vero progetto in esecuzione che utilizza SP, controlla minibooks . Buona parte dell'SQL personalizzato e utilizza Postgres pl / pgsql per SP. Penso che alla fine rimuoveranno l'SP (giustificazione in trac ticket 92 ).
Non farlo.
Scherzi a parte.
Sposta la logica della procedura memorizzata nel modello a cui appartiene.
Inserire un po 'di codice in Django e un po' di codice nel database è un incubo per la manutenzione. Ho trascorso troppi dei miei oltre 30 anni in IT cercando di ripulire questo tipo di pasticcio.
Suppongo che il supporto del queryset sql migliorato in Django 1.2 possa renderlo più semplice in quanto non dovresti creare il tuo codice di tipo make_instance.