您如何将列名称干净地传递到游标中,Python/SQLite?

How do you cleanly pass column names into cursor, Python/SQLite?

我是游标的新手,我正在尝试通过使用针对 sqlite3:[=12= 的清理方法构建动态 python sql 插入语句来练习]

import sqlite3
conn = sqlite3.connect("db.sqlite")
cursor = conn.cursor()
list = ['column1', 'column2', 'column3', 'value1', 'value2', 'value3']
cursor.execute("""insert into table_name (?,?,?) 
values (?,?,?)""", list)

当我尝试使用它时,在值所在的行出现语法错误 "sqlite3.OperationalError: near "?""。尽管事实上当我对列进行硬编码(并从列表中删除列名)时,我没有问题。我可以用 %s 构建,但我知道首选经过消毒的方法。

如何干净地插入这些?还是我遗漏了一些明显的东西?

首先,我将从创建列和值的映射开始:

data = {'column1': 'value1', 'column2': 'value2', 'column3': 'value3'}

然后,从这里获取列:

columns = data.keys()  
# ['column1', 'column3', 'column2']

现在,我们需要为列和值创建占位符:

placeholder_columns = ", ".join(data.keys())
# 'column1, column3, column2'

placeholder_values = ", ".join([":{0}".format(col) for col in columns])
# ':column1, :column3, :column2'

然后,我们创建 INSERT SQL 语句:

sql = "INSERT INTO table_name ({placeholder_columns}) VALUES ({placeholder_values})".format(
    placeholder_columns=placeholder_columns,
    placeholder_values=placeholder_values
)

# 'INSERT INTO table_name (column1, column3, column2) VALUES (:column1, :column3, :column2)'

现在,我们在 sql 中拥有的是带有命名参数的有效 SQL 语句。现在您可以使用数据执行此 SQL 查询:

cursor.execute(sql, data)

而且,由于 data 有键和值,它将使用查询中的命名占位符将值插入正确的列中。

查看 documentation 以了解如何使用命名参数。据我所知,您只需要担心插入值的清理。并且,有两种方法可以做到这一点 1) 使用问号样式,或 2) 命名参数样式。

(?, ?, ?) 语法仅适用于包含值的元组,恕我直言...这就是 sqlite3.OperationalError

的原因

我相信(!)您应该像这样构建它:

cursor.execute("INSERT INTO {tn} ({f1}, {f2}) VALUES (?, ?)".format(tn='testable', f1='foo', f1='bar'), ('test', 'test2',))

但是如果允许用户自己提供表名或字段名,这并不能解决注入问题。

我不知道有什么内置方法可以帮助解决这个问题。但是你可以使用这样的函数:

def clean(some_string):
    return ''.join(char for char in some_string if char.isalnum())

清理用户给定的表名或字段名。这应该足够了,因为 table-/fieldnames 通常只包含字母数字字符。

如果

,也许检查一下可能更聪明
some_string == clean(some_string)

如果为 False,为了安全起见,放弃一个很好的例外。

在我与 sql 和 python 合作期间,我觉得您不需要让用户自己命名他的表和字段名。所以它 is/was 对我来说很少需要。

如果有人能详细说明并发表他的见解,我将不胜感激。

    import sqlite3
    conn = sqlite3.connect("db.sqlite")
    cursor = conn.cursor()
## break your list into two, one for  column and one for value
    list = ['column1', 'column2', 'column3']
    list2= ['value1', 'value2', 'value3']
    cursor.execute("""insert into table_name("""+list[0]+""","""+list[1]+""","""+list[2]+""")
    values ("""+list2[0]+""","""+list2[1]+""","""+list2[2]+""")""")

所以,这就是我最终实现的,我认为它非常 pythonic,但如果没有 Krysopath 的洞察力,我无法回答它:

    columns = ['column1', 'column2', 'column3']
    values = ['value1', 'value2', 'value3']
    columns = ', '.join(columns)
    insertString=("insert into table_name (%s) values (?,?,?,?)" %columns)
    cursor.execute(insertString, values)