在 Django 的 ORM 中访问存储过程的最佳方法是啥
Posted
技术标签:
【中文标题】在 Django 的 ORM 中访问存储过程的最佳方法是啥【英文标题】:What is the best way to access stored procedures in Django's ORM在 Django 的 ORM 中访问存储过程的最佳方法是什么 【发布时间】:2010-10-22 18:25:12 【问题描述】:我正在设计一个相当复杂的数据库,并且知道我的一些查询将远远超出 Django 的 ORM 的范围。有没有人成功地将 SP 与 Django 的 ORM 集成?如果是这样,什么 RDBMS 以及您是如何做到的?
【问题讨论】:
Django Using Stored Procedure 会给出一些想法。 【参考方案1】:你必须在 Django 中使用连接工具:
from django.db import connection
with connection.cursor() as cursor:
cursor.execute("SQL STATEMENT CAN BE ANYTHING")
data = cursor.fetchone()
如果您期望不止一行,请使用 cursor.fetchall()
获取它们的列表。
更多信息在这里:http://docs.djangoproject.com/en/dev/topics/db/sql/
【讨论】:
【参考方案2】:可以使用Cx_Oracle。此外,当我们无法访问生产部署的代码并且需要对数据库进行重大更改时,它也很有帮助。
import cx_Oracle
try:
db = dev_plng_con
con = cx_Oracle.connect(db)
cur = con.cursor()
P_ERROR = str(error)
cur.callproc('NAME_OF_PACKAGE.PROCEDURENAME', [P_ERROR])
except Exception as error:
error_logger.error(message)
【讨论】:
【参考方案3】:有一个很好的例子: 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()
【讨论】:
【参考方案4】:我们(musicpictures.com / eviscape.com)编写了 django sn-p,但它并不是故事的全部(实际上该代码当时仅在 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
# 像这样使用它:
pictures = [make_instance(Pictures(), item) for item in picture_dict]
# 这里有一些辅助函数:
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]
干杯,西蒙。
【讨论】:
fn_generic
中的光标为什么要关闭?
我在一个巨大的系统上工作,该系统有一个可供多个应用程序访问的数据库,一些 c++,一些 python,一些 perl,一些 php,一些基于 web,很多不是。当业务逻辑在 SP 中时,我喜欢它,因为这意味着逻辑在所有实现中都是一致的,并且至少在我们的例子中,使维护变得更加容易。
我发现了 russ magee 的评论:“我们特别避免在 Django 的 ORM 中添加明显的类似 SQL 的功能,因为归根结底,我们并没有试图替换 SQL - 我们”只是试图提供一种方便的方式来表达简单的查询。完全可以预料到您将退回到只为复杂的情况调用原始 SQL”【参考方案5】:
我想 Django 1.2 中改进的原始 sql 查询集支持可以使这更容易,因为您不必滚动自己的 make_instance 类型代码。
【讨论】:
【参考方案6】:不要。
说真的。
将存储过程逻辑移到它所属的模型中。
将一些代码放在 Django 中,将一些代码放在数据库中是维护的噩梦。我在 IT 行业 30 多年的时间里花了太多时间试图清理这种烂摊子。
【讨论】:
@S.Lott 我认为你误解了我的观点。我说的是一个虚构的/未来的 Django ORM。存储过程不会由开发人员编写。此 ORM 将动态/透明地将常用的 ORM 查询转换为存储过程,从而可以节省 SQL 字符串生成时间并利用 SP 的预编译特性。同样,我并没有声称这甚至是可能的,或者值得加速。只是指出一个有趣的想法,他的问题对我产生了影响。这种方法可以将所有逻辑留在代码中,并具有 SP 性能。 @S.洛特这不是“神奇的”。生成“EXEC some_sp_name(with, params)”比生成大 SQL 语句要快。你可能会说,“嗯,这只是字符串,它超级快”。是的,但是如果你已经达到了 django 的 ORM SQL 生成的顶峰,我想你会发现它比这更可怕。此外,存储过程还利用了预编译的 SQL,例如参数化查询。我同意存储过程非常糟糕,但您必须承认,让 ORM 透明地为您生成它们而不是每次都生成 SQL 是一个有趣的想法。 @Chad:我说所有 SP 都是一个坏主意。统一。全面的。代码现在在两个地方。从长远来看,这样分割代码似乎永远不会奏效。 @S.Lott 所以30年后你可以说统一和整体存储过程是一个坏主意吗?哇,这涵盖了很多案例,我一秒钟都不会买它。那里有很多案例,我无法想象您个人可以证明所有这些案例。只是我的2美分。我可以想出许多他们认为完美的案例和其他没有意义的案例。只是为了记录在一些我可以想象或已经工作过的情况下,我完全同意你的观点,而在其他情况下,从你的角度来看,我是在宇宙的另一端。 @S.Lott 你仍然误解了我想说的话。代码仍然在一个地方。所有代码只存在于 Python 中的 ORM 逻辑中。我说的存储过程不是程序员写的。当 ORM 注意到某个确切的查询被大量发送时,作为微优化,ORM 会动态创建一个新的存储过程并使用它,而不是每次都生成 SQL。没有碎片代码!开发人员只编写 Python 代码,SP 的所有“好处”都可以透明地获得,而无需编写任何 SP 或“分割”您的业务逻辑。【参考方案7】:如果您想查看使用 SP 的实际运行项目,请查看minibooks。大量自定义 SQL 并使用 Postgres pl/pgsql for SP。我认为他们最终会删除 SP(trac ticket 92 中的理由)。
【讨论】:
以上是关于在 Django 的 ORM 中访问存储过程的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章
最佳实践:通过存储在 DB 中的方案在运行时创建 ORM 对象