2009-04-30 65 views

Respuesta

19

Nosotros (musicpictures.com/eviscape.com) escribimos ese fragmento de django pero no es toda la historia (de hecho, ese código solo fue probado en Oracle en ese momento).

Los procedimientos almacenados tienen sentido cuando se quiere reutilizar código SP probado y probado o cuando una llamada SP será más rápida que múltiples llamadas a la base de datos o donde la seguridad requiere acceso moderado a la base de datos o donde las consultas son muy complicadas/multipaso Estamos utilizando un enfoque de modelo híbrido/SP frente a las bases de datos Oracle y Postgres.

El truco está en hacer que sea fácil de usar y mantenerlo como "django". Utilizamos una función make_instance que toma el resultado del cursor y crea instancias de un modelo poblado por el cursor. Esto es bueno porque el cursor puede devolver campos adicionales. Entonces puede usar esas instancias en su código/plantillas al igual que los objetos normales del modelo 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 

# utilizar de esta manera:

pictures = [make_instance(Pictures(), item) for item in picture_dict] 

# Y aquí están algunas funciones auxiliares:

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]  

aplausos, Simon.

+0

¿Por qué cierra el cursor en 'fn_generic'? –

+2

Trabajo en un sistema inmenso con una base de datos a la que acceden múltiples aplicaciones, algunas C++, algunas python, algunas perl, algunas php, algunas basadas en web, muchas no. Me encanta cuando la lógica comercial está en los SP porque significa que la lógica es consistente en todas las implementaciones, y en nuestro caso al menos, hace que el mantenimiento sea mucho más fácil. –

+4

he encontrado este comentario por russ magee: "Hemos evitado específicamente agregar características obvias similares a SQL a ORM de Django, porque al final del día, no estamos tratando de reemplazar SQL - solo estamos tratando de proporciona una forma conveniente de expresar consultas simples. Se espera que recurras simplemente a SQL sin formato para casos complejos ". –

15

usted tiene que utilizar la Utilidad de conexión en Django:

from django.db import connection 

cursor = connection.cursor() 
cursor.execute("SQL STATEMENT CAN BE ANYTHING") 

entonces se puede recuperar los datos:

cursor.fetchone() 

o:

cursor.fetchall() 

Más información aquí: http://docs.djangoproject.com/en/dev/topics/db/sql/

2

Si desea ver un proyecto en ejecución real que utiliza SP, consulte minibooks. Una buena cantidad de SQL personalizado y utiliza Postgres pl/pgsql para SP. Creo que van a eliminar el SP eventualmente (justificación en trac ticket 92).

0

No.

En serio.

Mueva la lógica del procedimiento almacenado a su modelo donde pertenece.

Poner un código en Django y algún código en la base de datos es una pesadilla de mantenimiento. He pasado demasiados de mis más de 30 años en TI tratando de limpiar este tipo de desorden.

+0

Lo que está preguntando es acerca de cómo integrar el ORM con los SP. Probablemente esto no sea posible, y el uso de procedimientos almacenados probablemente requiera que accedas a django.db.connection directamente como en las otras respuestas, pero sería interesante si pudieras mover automáticamente consultas comunes que el ORM realiza a procedimientos almacenados, para guardar en el tiempo de generación de consultas, y hacerlo de forma transparente, como una optimización. No funcionaría en todas las bases de datos, y la ganancia de rendimiento probablemente no valga la pena, pero sería divertido investigar. – Chad

+0

@Chad: "Lo que está preguntando es sobre la integración del ORM con los SP". Entendido. De ahí mi respuesta. Los SP fragmentan la lógica de la aplicación entre el código de aplicación adecuado y la base de datos. A menudo crean más problemas de los que resuelven. Creo que los SP no son útiles bajo ninguna circunstancia y no deberían usarse. –

+2

@ S.Lott Creo que malinterpretaste el punto que hice. Estoy hablando de un futuro/imaginario Django ORM. Los procedimientos almacenados no serán escritos por los desarrolladores. Este ORM convertirá dinámicamente/transparentemente las consultas ORM ejecutadas comúnmente en procedimientos almacenados, de modo que pueda ahorrar tiempo de generación de cadenas SQL y hacer uso de la naturaleza precompilada de SP. Una vez más, no pretendo pensar que esto sea siquiera posible, o que valdría la pena la aceleración. Solo señalé una idea interesante que su pregunta generó para mí. Este enfoque podría dejar toda la lógica en el código y tener un rendimiento de SP. – Chad

0

Supongo que la compatibilidad mejorada con el SQLSQL rawset en Django 1.2 puede hacer esto más fácil ya que no tendría que lanzar su propio código de tipo make_instance.

3

No es un buen ejemplo: 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() 
Cuestiones relacionadas