2009-04-30 5 views

risposta

19

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.

+0

Perché chiudi il cursore in 'fn_generic'? –

+2

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. –

+4

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 " –

15

è 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/

2

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).

0

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.

+0

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

+0

@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. –

+2

@ 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

0

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.

3

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()