<p><sup>老问题,但添加一些与Python配方相关的有用链接</p>
<p>根据<code>cx_Oracle</code>文档:</p>
<blockquote>
<h3><a href="http://cx-oracle.readthedocs.io/en/latest/cursor.html#Cursor.rowfactory" rel="nofollow noreferrer">Cursor.rowfactory</a></h3>
<p>This read-write attribute specifies a method to call for each row that
is retrieved from the database. <strong>Ordinarily a tuple is returned</strong> for
each row but <strong>if this attribute is set, the method is called with the
tuple that would normally be returned, and the result of the method is
returned instead</strong>.</p>
</blockquote>
<p><a href="https://oracle.github.io/python-cx_Oracle/" rel="nofollow noreferrer">cx_Oracle - Python Interface for Oracle Database</a>还指向GitHub存储库以获取许多有用的<a href="https://github.com/oracle/python-cx_Oracle" rel="nofollow noreferrer">sample</a>示例。请检查<a href="https://github.com/oracle/python-cx_Oracle/blob/master/samples/GenericRowFactory.py" rel="nofollow noreferrer">GenericRowFactory.py</a>。</p>
<p>谷歌:这个PPT可以进一步帮助:<a href="https://static.rainfocus.com/oracle/oow16/sess/1464899218456001XoGM/ppt/201609-OOW-CON6543-Python-Oracle-Database.pdf" rel="nofollow noreferrer"><sup>[PDF]</sup>CON6543 Python and Oracle Database - RainFocus</a></p>
<h3>配方</h3>
<p>Django数据库后台为Oracle under the hood使用的是cx_Oracle。在早期的版本(Django 1.11-)中,他们编写了<a href="https://chromium.googlesource.com/external/googleappengine/python/+/4523f63814037ed833b8bb55273c09a43dd69fb0/lib/django-1.5/django/db/backends/oracle/base.py#795" rel="nofollow noreferrer"><strong>^{<cd2>}</strong></a>,还将cx_Oracle的数字数据类型转换为相关的Python数据,并将字符串转换为unicode。</p>
<p>如果您安装了Django,请按以下方式检查base.py:</p>
<pre><code>$ DJANGO_DIR="$(python -c 'import django, os; print(os.path.dirname(django.__file__))')"
$ vim $DJANGO_DIR/db/backends/oracle/base.py
</code></pre>
<p>可以从<code>$DJANGO_DIR/db/backends/oracle/base.py</code>中借用<code>_rowfactory()</code>,并可以在decorator <code>naming</code>下面应用,使其返回<code>namedtuple</code>,而不是简单的<code>tuple</code>。</p>
<p>mybase.py版</p>
<pre><code>import functools
from itertools import izip, imap
from operator import itemgetter
from collections import namedtuple
import cx_Oracle as Database
import decimal
def naming(rename=False, case=None):
def decorator(rowfactory):
@functools.wraps(rowfactory)
def decorated_rowfactory(cursor, row, typename="GenericRow"):
field_names = imap(case, imap(itemgetter(0), cursor.description))
return namedtuple(typename, field_names)._make(rowfactory(cursor, row))
return decorated_rowfactory
return decorator
</code></pre>
<p>用作:</p>
<pre><code>@naming(rename=False, case=str.lower)
def rowfactory(cursor, row):
casted = []
....
....
return tuple(casted)
</code></pre>
<p>甲骨文.py</p>
<pre><code>import cx_Oracle as Database
from cx_Oracle import *
import mybase
class Cursor(Database.Cursor):
def execute(self, statement, args=None):
prepareNested = (statement is not None and self.statement != statement)
result = super(self.__class__, self).execute(statement, args or [])
if prepareNested:
if self.description:
self.rowfactory = lambda *row: mybase.rowfactory(self, row)
return result
def close(self):
try:
super(self.__class__, self).close()
except Database.InterfaceError:
"already closed"
class Connection(Database.Connection):
def cursor(self):
Cursor(self)
connect = Connection
</code></pre>
<p>现在,不要在用户脚本中导入cx_oracle导入oracle:</p>
<p>用户.py</p>
<pre><code>import oracle
dsn = oracle.makedsn('HOSTNAME', 1521, service_name='dev_server')
db = connect('username', 'password', dsn)
cursor = db.cursor()
cursor.execute("""
SELECT 'Grijesh' as FirstName,
'Chauhan' as LastName,
CAST('10560.254' AS NUMBER(10, 2)) as Salary
FROM DUAL
""")
row = cursor.fetchone()
print ("First Name is %s" % row.firstname) # => Grijesh
print ("Last Name is %s" % row.lastname) # => Chauhan
print ("Salary is %r" % row.salary) # => Decimal('10560.25')
</code></pre>
<p>试试看!!</p>