如何强制 Django 使用服务名称连接到 Oracle

Posted

技术标签:

【中文标题】如何强制 Django 使用服务名称连接到 Oracle【英文标题】:How do I force Django to connect to Oracle using Service Name 【发布时间】:2013-10-15 07:31:27 【问题描述】:

问:如何指定 Django 需要使用服务名称而不是 SID 连接到 Oracle DB?

嗨,

我目前正在告诉我的 Django 配置使用我的 SID 连接到 Oracle。

但是,我需要使用服务名称而不是 SID 进行连接。

APP_DATABASES=
    'default': 
            'ENGINE': 'django.db.backends.oracle',
            'NAME': 'myservice',
            'USER': 'system',
            'PASSWORD': 'admin123',
            'HOST': '192.168.1.45',
            'PORT': '1699',
    

这很好用。

但是,当我将“NAME”替换为服务名称时,如下所示

 'default': 
                'ENGINE': 'django.db.backends.oracle',
                'NAME': 'myservice.bose.com',
                'USER': 'system',
                'PASSWORD': 'admin123',
                'HOST': '192.168.1.45',
                'PORT': '1699',
        

我得到一个

ORA-12505: TNS:listener does not currently know of SID given in connect descriptor

显然 Django 告诉 Oracle 使用 SID 进行连接,这不是我希望 Django 做的。

如何指定 Django 需要使用服务而不是 SID 连接到 Oracle DB?

注意:我已经测试过上面提到的服务名称。它在 Oracle SQL Developer 中运行良好。

谢谢 - 非常感谢潜在客户。

【问题讨论】:

【参考方案1】:

以下是工作方式:

myservice =
 (DESCRIPTION = 
   (ADDRESS_LIST =
     (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.45)(PORT = 1699))
   )
 (CONNECT_DATA =
   (SERVICE_NAME = myservice_name)
 )
)

myservice = ''.join(trc_scan.split())

'default': 
  'ENGINE': 'django.db.backends.oracle',
  'NAME': myservice,
  'USER': 'someuser',
  'PASSWORD': 'somepsw',

请勿在用户名和/或密码中输入以下任何字符:

@/(

【讨论】:

【参考方案2】:

我使用 tnsnames.ora。它适用于 Django 1.7。 这些是步骤:

    在 tnsnames.ora 中为连接添加一个条目。

    myservice =
     (DESCRIPTION = 
       (ADDRESS_LIST =
         (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.45)(PORT = 1699))
       )
     (CONNECT_DATA =
       (SERVICE_NAME = myservice.bose.com)
     )
    )
    

    将 Django 数据库设置更改为

    'default': 
      'ENGINE': 'django.db.backends.oracle',
      'NAME': 'myservice',
      'USER': 'system',
      'PASSWORD': 'admin123',
    
    

详情请参考Connecting Django to Oracle database using service name

【讨论】:

【参考方案3】:

谢谢大家,有一个“记录在案”的解决方案:

        'default': 
                'ENGINE': 'django.db.backends.oracle',
                'NAME': 'host.db.com:1699/oracle_service.db.com',
                'USER': 'user',
                'PASSWORD': 'pass',
        

注意:HOST 和 PORT 键需要从字典中排除 - 否则 Django 将尝试使用完整的“NAME”作为 SID 进行连接。

【讨论】:

这个解决方案对我有用 Debian 7 / oracle instantclient 12.1.0.2.0 / Django 1.4。 如果您使用django-environ,请使用oracle://user:pass@/host.db.com:1699/oracle_service.db.com(成功了)【参考方案4】:

查看 nickzam 粘贴的代码:

import cx_Oracle as Database

def _connect_string(self):
    settings_dict = self.settings_dict
    if not settings_dict['HOST'].strip():
        settings_dict['HOST'] = 'localhost'
    if settings_dict['PORT'].strip():
        dsn = Database.makedsn(settings_dict['HOST'],
                               int(settings_dict['PORT']),
                               settings_dict['NAME'])
    else:
        dsn = settings_dict['NAME']
    return "%s/%s@%s" % (settings_dict['USER'],
                         settings_dict['PASSWORD'], dsn)

.. 很明显,如果您不指定“PORT”参数,“NAME”参数将“按原样”使用。因此,将 Oracle 连接字符串作为“NAME”参数传递就可以解决问题(如果删除“PORT”参数)。

基本上这样的事情会起作用:

'default': 
    'ENGINE': 'oraclepool',
    'NAME': '(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=mydbhostname.example.com)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=myservicename.example.com)))',
    'USER': 'scott',
    'PASSWORD': 'tiger',

我已经尝试使用 SCAN 主机名作为 HOST 并验证这也有效。

警告:到目前为止,我的测试仅限于检查连接字符串是否被接受、连接是否建立以及我的应用程序是否成功服务、访问数据。在依赖此配置之前,我建议进行更积极的测试 8)

【讨论】:

这真是一种诡计。请记住,“显式优于隐式” 我同意。话虽如此,在提供支持的解决方案之前,此解决方案必须比破解 base.py 更好 8) 嘿,谢谢 - 这是一个有趣的方法 :) 查看我对自己问题的回答。【参考方案5】:

在幕后 Django 使用 cx_Oracle 库连接到 Oracle 数据库。 来源:https://github.com/django/django/blob/master/django/db/backends/oracle/base.py

import cx_Oracle as Database

def _connect_string(self):
        settings_dict = self.settings_dict
        if not settings_dict['HOST'].strip():
            settings_dict['HOST'] = 'localhost'
        if settings_dict['PORT'].strip():
            dsn = Database.makedsn(settings_dict['HOST'],
                                   int(settings_dict['PORT']),
                                   settings_dict['NAME'])
        else:
            dsn = settings_dict['NAME']
        return "%s/%s@%s" % (settings_dict['USER'],
                             settings_dict['PASSWORD'], dsn)

函数 cx_Oracle.make_dsn() 支持可选参数 service_name(摘自 cx_Oracle 文档):

cx_Oracle.makedsn(host, port, sid[, service_name])

Return a string suitable for use as the dsn for the connect() method. This string is identical to the strings that are defined by the Oracle names server or defined in the tnsnames.ora file. If you wish to use the service name instead of the sid, do not include a value for the parameter sid and use the keyword parameter service_name instead. Note This method is an extension to the DB API definition.

不幸的是,Django 没有在连接时传递service_name 参数。

如果你真的需要它,给 Django 添加功能请求或修补你本地版本的 Django 以支持 SERVICE_NAME 参数(坏主意,你需要自己支持它):

def _connect_string(self):
    settings_dict = self.settings_dict
    if not settings_dict['HOST'].strip():
        settings_dict['HOST'] = 'localhost'
    if settings_dict['PORT'].strip():
        if not 'SERVICE_NAME' in settings_dict:
            dsn = Database.makedsn(settings_dict['HOST'],
                                   int(settings_dict['PORT']),
                                   settings_dict['NAME'])
        else:
            dsn = Database.makedsn(host=settings_dict['HOST'],
                                   port=int(settings_dict['PORT']),
                                   service_name=settings_dict['SERVICE_NAME'].strip())

    else:
        dsn = settings_dict['NAME']
    return "%s/%s@%s" % (settings_dict['USER'],
                         settings_dict['PASSWORD'], dsn)

然后将NAME 更改为SERVICE_NAME 变量为您的连接“默认”:

 'default': 
            'ENGINE': 'django.db.backends.oracle',
            'SERVICE_NAME': 'myservice.bose.com',
            'USER': 'system',
            'PASSWORD': 'admin123',
            'HOST': '192.168.1.45',
            'PORT': '1699',
    

稍后,我会将其作为拉取请求添加到 Django 源代码。

【讨论】:

以上是关于如何强制 Django 使用服务名称连接到 Oracle的主要内容,如果未能解决你的问题,请参考以下文章

C#如何强制等待连接到 WCF 服务

重新连接到服务器时如何强制 Atmosphere.js 使用首选传输?

如何使用 Windows 将我的 Django 项目连接到 MySql?

ORA-01034: ORACLE 不可用 ORA-27101: 共享内存领域不存在

无法使用 sqlplus 连接到数据库:ORA-12154:TNS:无法解析指定的连接标识符

尝试使用LINQPad连接到Oracle,提供“无法获取Oracle客户端信息”