Как заставить Django подключаться к Oracle с использованием имени службы

В: Как указать, что Django необходимо подключиться к Oracle DB с использованием имени службы, а не SID?

Привет,

В настоящее время я говорю, что моя конфигурация Django подключается к Oracle с использованием моего SID.

Однако мне нужно будет подключиться, используя имя службы, а не идентификатор 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 необходимо подключиться к Oracle DB с помощью службы, а не SID?

Примечание. Я тестировал это имя службы, упомянутое выше. Он отлично работает с Oracle SQL Developer.

Спасибо - поистине оценил бы лидеры.

Ответ 1

Спасибо, ребята, Там есть "задокументированное" решение:

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

Примечание. Клавиши HOST и PORT должны быть исключены из словаря - иначе Django попытается подключиться к полному "NAME" как SID.

Ответ 2

Глядя на код, который вставляем 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/%[email protected]%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)

Ответ 3

За кулисами 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/%[email protected]%s" % (settings_dict['USER'],
                             settings_dict['PASSWORD'], dsn)

Функция cx_Oracle.make_dsn() поддерживает необязательный параметр service_name (выдержка из cx_Oracle docs):

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/%[email protected]%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.

Ответ 4

Я использую tnsnames.ora. У меня работает на Django 1.7. Вот эти шаги:

  1. Добавьте запись в tnsnames.ora для подключения.

    myservice =
     (DESCRIPTION = 
       (ADDRESS_LIST =
         (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.45)(PORT = 1699))
       )
     (CONNECT_DATA =
       (SERVICE_NAME = myservice.bose.com)
     )
    )
    
  2. Изменить настройки базы данных Django на

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

Для получения подробной информации, пожалуйста, обратитесь к Соединению Django с базой данных Oracle, используя имя сервиса