简体   繁体   English

Python 3.6-“字符串%元组”做什么?

[英]Python 3.6 - What does “string % tuple” do?

I am writing a python module for an Django-based application that accesses an Oracle database via cx_Oracle. 我正在为基于Django的应用程序编写一个python模块,该模块通过cx_Oracle访问Oracle数据库。 It "appears" that the django code has a bug that breaks the use of the cx_Oracle "executemany" method. django代码似乎“出现”了一个错误,该错误打破了对cx_Oracle“ executemany”方法的使用。 If I use cx_Oracle with a connection opened strictly via cx_Oracle, the logic works fine. 如果我通过严格通过cx_Oracle打开的连接使用cx_Oracle,则逻辑工作正常。 Use a connection via django, it fails. 通过django使用连接,失败。

As django is a requirement, I am looking for a work-around and need to understand what the statement (below) where it fails is trying to do. 由于django是必需项,因此我正在寻找一种解决方法,并且需要了解失败的语句(以下)试图执行的操作。 I understand that "%" is used both as a modulo operator and for string formatting as it apparently is in this case. 我知道“%”既可以用作模运算符,也可以用于字符串格式化,因为在这种情况下显然是这样。 But despite much searching, this doesn't seem to conform to any string formatting syntax using "%" that I can find. 但是,尽管进行了大量搜索,但这似乎并不符合我可以找到的使用“%”的任何字符串格式语法。 Can someone explain what this is trying to do? 有人可以解释这是做什么的吗?

query = query % tuple(args)

TypeError: not all arguments converted during string formatting

Where: 哪里:

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']

If you type these values and the above statement into the REPL, you will get this same error. 如果在REPL中键入这些值和上面的语句,您将得到同样的错误。

I know I should submit a django bug report. 我知道我应该提交django错误报告。 Will figure that out later. 稍后会发现。 For now I'm hoping I can somehow change the Oracle bind-variable positional notation in the query string to satisfy the above statement. 现在,我希望可以以某种方式更改查询字符串中的Oracle绑定变量位置表示法,以满足上述要求。 Again, the query string has no problem working directly with cx_Oracle. 同样,查询字符串在直接与cx_Oracle一起使用时没有问题。

Details: 细节:

Python 3.6.5 :: Anaconda, Inc. Python 3.6.5 :: Anaconda,Inc.

cx-Oracle 7.0.0 cx-Oracle 7.0.0

Django 2.0.7 的Django 2.0.7

cx_Oracle query format: https://www.oracle.com/technetwork/articles/dsl/prez-python-queries-101587.html (See "Many at Once") cx_Oracle查询格式: https : //www.oracle.com/technetwork/articles/dsl/prez-python-queries-101587.html (请参阅“一次很多”)

My cx_Oracle code: 我的cx_Oracle代码:

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

The code that's failing is in django module base.py, line 494: (C:\\python\\Anaconda2\\envs\\py36\\lib\\site-packages\\django\\db\\backends\\oracle\\base.py) 失败的代码在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)

To enable cross-database compatibility, Django uses uniform placeholders across all database backends. 为了实现跨数据库兼容性,Django在所有数据库后端使用统一的占位符。 The code you pasted translates %s placeholders to Oracle-specific placeholders, but it fails because your query is already using Oracle-specific placeholders. 您粘贴的代码将%s占位符转换为Oracle特定的占位符,但是失败,因为您的查询已经在使用Oracle特定的占位符。

Replace the placeholders with %s and it should work: %s替换占位符,它应该可以工作:

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

% is the string formatting operator , which applies item values in the tuple that follows to the placeholders (that start with a % ) in the formatting string that precedes it. %字符串格式运算符 ,它在元组中的项目值应用于其前面的格式字符串中的占位符(以%开头)。

Since your query string does not actually contain any formatting placeholders that start with a % , the % operator fails because it can't map the values in the tuple to placeholders when there isn't any placeholder. 由于您的query字符串实际上不包含任何以%开头的格式占位符,因此%运算符将失败,因为当没有任何占位符时,它无法将元组中的值映射到占位符。 For your purpose you should pass args as a parameter to the execute method of your database cursor, so that the argument values can be applied to the bind variables (denoted by :1 , :2 , etc. in the query string) in a secure manner. 为了您的目的,应将args作为参数传递给数据库游标的execute方法,以便可以安全地将参数值应用于绑定变量(由query字符串中的:1:2等表示)。方式。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM