Python 3.6 — что делает кортеж строки%?

Я пишу модуль Python для приложения на основе Django, которое обращается к базе данных Oracle через cx_Oracle. «Похоже», что в коде django есть ошибка, которая нарушает использование метода «executemany» cx_Oracle. Если я использую cx_Oracle с подключением, открытым строго через cx_Oracle, логика работает нормально. Используйте соединение через django, оно не работает.

Поскольку django является требованием, я ищу обходной путь, и мне нужно понять, что пытается сделать оператор (ниже), в котором он терпит неудачу. Я понимаю, что "%" используется как оператор по модулю, так и для форматирования строки, как в данном случае. Но, несмотря на долгие поиски, это, похоже, не соответствует какому-либо синтаксису форматирования строк с использованием «%», который я могу найти. Может кто-нибудь объяснить, что это пытается сделать?

query = query % tuple(args)

TypeError: not all arguments converted during string formatting

Где:

query = 'INSERT INTO DATABASE.TABLE\n        (DATE, ID, COL_A, COL_B, COL_C)\n    VALUES (:1, :2, :3, :4, :5)\n'

args = [':arg0', ':arg1', ':arg2', ':arg3', ':arg4']

Если вы введете эти значения и приведенный выше оператор в REPL, вы получите ту же ошибку.

Я знаю, что должен отправить отчет об ошибке django. Разберусь с этим позже. На данный момент я надеюсь, что смогу каким-то образом изменить позиционную нотацию переменной связывания Oracle в строке запроса, чтобы удовлетворить приведенному выше утверждению. Опять же, строка запроса без проблем работает напрямую с cx_Oracle.

Подробности:

Python 3.6.5 :: Anaconda, Inc.

cx-Оракул 7.0.0

Джанго 2.0.7

Формат запроса cx_Oracle: https://www.oracle.com/technetwork/articles/dsl/prez-python-queries-101587.html (см. «Несколько сразу»)

Мой код cx_Oracle:

cursor = conn.cursor()
cursor.prepare(query)
cursor.executemany(query, list_of_tuples_of_values)
rows_affected = cursor.rowcount
conn.commit()

Неудачный код находится в модуле django base.py, строка 494: (C:\python\Anaconda2\envs\py36\lib\site-packages\django\db\backends\oracle\base.py)

def _fix_for_params(self, query, params, unify_by_values=False):
    # cx_Oracle wants no trailing ';' for SQL statements.  For PL/SQL, it
    # it does want a trailing ';' but not a trailing '/'.  However, these
    # characters must be included in the original query in case the query
    # is being passed to SQL*Plus.
    if query.endswith(';') or query.endswith('/'):
        query = query[:-1]
    if params is None:
        params = []
    elif hasattr(params, 'keys'):
        # Handle params as dict
        args = {k: ":%s" % k for k in params}
        query = query % args
    elif unify_by_values and len(params) > 0:
        # Handle params as a dict with unified query parameters by their
        # values. It can be used only in single query execute() because
        # executemany() shares the formatted query with each of the params
        # list. e.g. for input params = [0.75, 2, 0.75, 'sth', 0.75]
        # params_dict = {0.75: ':arg0', 2: ':arg1', 'sth': ':arg2'}
        # args = [':arg0', ':arg1', ':arg0', ':arg2', ':arg0']
        # params = {':arg0': 0.75, ':arg1': 2, ':arg2': 'sth'}
        params_dict = {param: ':arg%d' % i for i, param in enumerate(set(params))}
        args = [params_dict[param] for param in params]
        params = {value: key for key, value in params_dict.items()}
        query = query % tuple(args)
    else:
        # Handle params as sequence
        args = [(':arg%d' % i) for i in range(len(params))]
        query = query % tuple(args)             <==============
    return query, self._format_params(params)

params = (datetime.datetime(2018, 10, 12, 0, 0), '123456', 10, 10, 8)

person user2285103    schedule 23.10.2018    source источник
comment
Никогда не используйте форматирование строк (f-строки, str.format() или %) в запросах SQL. Таким образом вы получите уязвимости SQL-инъекций.   -  person Klaus D.    schedule 23.10.2018


Ответы (2)


Чтобы обеспечить совместимость между базами данных, Django использует единые заполнители для всех серверных частей базы данных. Вставленный код преобразует %s заполнителей в заполнители, специфичные для Oracle, но это не удается, поскольку в вашем запросе уже используются заполнители, специфичные для Oracle.

Замените заполнители на %s, и все должно работать:

 query = 'INSERT INTO DATABASE.TABLE\n        (DATE, ID, COL_A, COL_B, COL_C)\n    VALUES (%s, %s, %s, %s, %s)\n'
person knbk    schedule 23.10.2018

% – это оператор форматирования строк, который применяет элемент значения в кортеже, который следует за заполнителями (начинающимися с %) в предшествующей строке форматирования.

Поскольку ваша строка query на самом деле не содержит заполнителей форматирования, начинающихся с %, оператор % не работает, потому что он не может сопоставить значения в кортеже с заполнителями, когда нет заполнителя. Для вашей цели вы должны передать args в качестве параметра методу execute вашего курсора базы данных, чтобы значения аргументов могли быть применены к переменным связывания (обозначаемым :1, :2 и т. д. в строке query) безопасным образом.

person blhsing    schedule 23.10.2018