在Django的ORM中访问存储过程的最佳方法是什么

kku*_*sik 25 python sql django stored-procedures django-models

我正在设计一个相当复杂的数据库,并且知道我的一些查询将远远超出Django的ORM范围.有没有人成功地将SP与Django的ORM集成在一起?如果是这样,什么是RDBMS,你是怎么做到的?

小智 23

我们(musicpictures.com/eviscape.com)写了django片段但不是整个故事(实际上那段代码当时只在Oracle上测试过).

当您想要重用经过试验和测试的SP代码,或者一个SP调用比多次调用数据库更快 - 或者安全性需要对数据库进行调节访问 - 或者查询非常复杂/多步骤时,存储过程才有意义.我们对Oracle和Postgres数据库使用混合模型/ SP方法.

诀窍是让它易于使用并保持"django"之类的.我们使用make_instance函数,它接受游标的结果并创建从游标填充的模型的实例.这很好,因为游标可能会返回其他字段.然后,您可以在代码/模板中使用这些实例,就像普通的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
Run Code Online (Sandbox Code Playgroud)

#像这样使用它:

pictures = [make_instance(Pictures(), item) for item in picture_dict]
Run Code Online (Sandbox Code Playgroud)

#这里有一些辅助函数:

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]    
Run Code Online (Sandbox Code Playgroud)

欢呼,西蒙.

  • 我发现russ magee的评论:"我们特别避免为Django的ORM添加明显的类似SQL的功能,因为在一天结束时,我们并没有尝试替换SQL - 我们只是试图提供一种方便的方式表达简单的查询.完全可以预期你会回到刚刚为复杂案例调用原始SQL" (4认同)
  • 我在一个巨大的系统上工作,数据库可以被多个应用程序访问,一些c ++,一些python,一些perl,一些php,一些web基础,很多都不是.我喜欢它,当业务逻辑在SP中时,因为它意味着逻辑在所有实现中都是一致的,至少在我们的例子中,使维护更容易. (2认同)

igo*_*gue 16

您必须在Django中使用连接实用程序:

from django.db import connection

cursor = connection.cursor()
cursor.execute("SQL STATEMENT CAN BE ANYTHING")
Run Code Online (Sandbox Code Playgroud)

然后你可以获取数据:

cursor.fetchone()
Run Code Online (Sandbox Code Playgroud)

要么:

cursor.fetchall()
Run Code Online (Sandbox Code Playgroud)

更多信息:http://docs.djangoproject.com/en/dev/topics/db/sql/


S.L*_*ott 6

别。

严重地。

将存储过程逻辑移到它所属的模型中。

将一些代码放在 Django 中,将一些代码放在数据库中是维护的噩梦。在我 30 多年的 IT 工作中,我花了太多时间试图清理这种烂摊子。

  • @S.Lott 那么在 30 年后,您可以说 Uniformly 和 Total 存储过程是一个坏主意吗?哇,涵盖了很多案例,我一秒钟都不买。那里有很多案例,我无法想象您个人可以证明所有案例。只是我的 2 美分。我可以想出很多他们认为完美的案例,而其他案例则不然。只是为了记录我可以想象或处理过的一些情况,我完全同意你的看法,而在其他情况下,从你的角度来看,我在宇宙的另一边。 (4认同)
  • @Chad:我说的所有 SP 都是一个坏主意。统一。总体。代码现在在两个地方。从长远来看,像这样分割代码似乎永远不会很好。 (3认同)
  • @S.Lott 我想你误解了我的观点。我说的是一个虚构的/未来的 Django ORM。存储过程不会由开发人员编写。该 ORM 将动态/透明地将经常执行的 ORM 查询转换为存储过程,从而可以节省 SQL 字符串生成时间并利用 SP 的预编译特性。再说一次,我并不是说这甚至是可能的,或者说加速是值得的。只是指出一个有趣的想法,他的问题为我产生了。这种方法可以将所有逻辑留在代码中并具有 SP 性能。 (2认同)

小智 5

有一个很好的例子: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()
Run Code Online (Sandbox Code Playgroud)