将Oracle服务名称与SQLAlchemy一起使用

时间:2013-01-03 14:31:27

标签: oracle sqlalchemy

我遇到了一个令人讨厌的小问题,通过SQLAlchemy使用服务名称连接到Oracle模式。这是我的代码作为脚本。 (出于安全原因,尖括号之间的项目是实际值的占位符)

from sqlalchemy import create_engine

if __name__ == "__main__":                                                                                                                                                        
    engine = create_engine("oracle+cx_oracle://<username>:<password>@<host>/devdb")                                                                                                                                                   
    result = engine.execute("create table test_table (id NUMBER(6), name VARCHAR2(15) not NULL)")
    result = engine.execute("drop table test_table")

其中'devdb'是服务名称而不是SID。运行此脚本的结果是堆栈跟踪。

(oracle-test)[1]jgoodell@jgoodell-MBP:python$ python example.py 
Traceback (most recent call last):
  File "example.py", line 8, in <module>
    result = engine.execute("create table test_table (id NUMBER(6), name VARCHAR2(15) not NULL)")
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/engine/base.py", line 1621, in execute
    connection = self.contextual_connect(close_with_result=True)
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/engine/base.py", line 1669, in contextual_connect
    self.pool.connect(),
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 272, in connect
    return _ConnectionFairy(self).checkout()
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 425, in __init__
    rec = self._connection_record = pool._do_get()
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 777, in _do_get
    con = self._create_connection()
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 225, in _create_connection
    return _ConnectionRecord(self)
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 318, in __init__
    self.connection = self.__connect()
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/pool.py", line 368, in __connect
    connection = self.__pool._creator()
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/engine/strategies.py", line 80, in connect
    return dialect.connect(*cargs, **cparams)
  File "/Users/jgoodell/.virtualenvs/oracle-test/lib/python2.6/site-packages/sqlalchemy/engine/default.py", line 279, in connect
    return self.dbapi.connect(*cargs, **cparams)
sqlalchemy.exc.DatabaseError: (DatabaseError) ORA-12505: TNS:listener does not currently know of SID given in connect descriptor
 None None

如果'devdb'是SID而不是服务名称,这个例子就可以正常工作了,我一直在尝试连接字符串的不同排列但是没有找到任何有用的东西。 SQLAlchemy文档中似乎没有任何内容明确说明如何处理SID的Oracle连接的服务名称。

4 个答案:

答案 0 :(得分:18)

我已经找到了你必须使用相同的连接字符串的答案,该连接字符串将在'@“之后的连接字符串中的tnsnames.ora文件中使用,如此

from sqlalchemy import create_engine

if __name__ == "__main__":                                                                                                                                                        
    engine = create_engine("oracle+cx_oracle://<username>:<password>@(DESCRIPTION = (LOAD_BALANCE=on) (FAILOVER=ON) (ADDRESS = (PROTOCOL = TCP)(HOST = <host>)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = devdb)))")                                                                                                                                                   
    result = engine.execute("create table test_table (id NUMBER(6), name VARCHAR2(15) not NULL)")
    result = engine.execute("drop table test_table")

此示例运行正常,您可以注释掉drop语句并检查数据库以查看该表是否已创建。

答案 1 :(得分:8)

import cx_Oracle
dsnStr = cx_Oracle.makedsn('myhost','port','MYSERVICENAME')
dsnStr = dsnStr.replace('SID', 'SERVICE_NAME')
connect_str = 'oracle://user:password@' + dnsStr

makedns将创建一个这样的TNS:

  

(DESCRIPTION =(ADDRESS_LIST =(ADDRESS =(PROTOCOL = TCP)(HOST =为myhost)(PORT = 1530)))(CONNECT_DATA =(SID =服务名)))

将“SID”替换为“SERVICE_TYPE”让它对我有效。

如果您使用flask,sqlalchemy和oracle:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import cx_Oracle

app = Flask(__name__)
dnsStr = cx_Oracle.makedsn('my.host.com', '1530', 'my.service.name')
dnsStr = dnsString.replace('SID', 'SERVICE_NAME')
app.config['SQLALCHEMY_DATABASE_URI'] = 'oracle://myschema:mypassword@' + dnsStr
db = SQLAlchemy(app)

答案 2 :(得分:4)

模块sqlalchemy现在可以处理oracle service_names。看看:

from sqlalchemy.engine import create_engine

DIALECT = 'oracle'
SQL_DRIVER = 'cx_oracle'
USERNAME = 'your_username' #enter your username
PASSWORD = 'your_password' #enter your password
HOST = 'subdomain.domain.tld' #enter the oracle db host url
PORT = 1521 # enter the oracle port number
SERVICE = 'your_oracle_service_name' # enter the oracle db service name
ENGINE_PATH_WIN_AUTH = DIALECT + '+' + SQL_DRIVER + '://' + USERNAME + ':' + PASSWORD +'@' + HOST + ':' + str(PORT) + '/?service_name=' + SERVICE

engine = create_engine(ENGINE_PATH_WIN_AUTH)


#test query
import pandas as pd
test_df = pd.read_sql_query('SELECT * FROM global_name', engine)

答案 3 :(得分:2)

cx_Oracle支持将service_name传递给makedsn函数。

http://cx-oracle.sourceforge.net/html/module.html?highlight=makedsn#cx_Oracle.makedsn

如果create_engine()API将service_name传递给它对makedsn进行的底层调用,那就太好了......就像这样:

oracle = create_engine('oracle://user:pw@host:port', service_name='myservice')

TypeError: Invalid argument(s) 'service_name' sent to create_engine(), using configuration OracleDialect_cx_oracle/QueuePool/Engine.
Please check that the keyword arguments are appropriate for this combination of components.