如何将列的默认值指定为占位符?

时间:2017-10-11 20:29:28

标签: python postgresql psycopg2

我有一个包含多列的表,其中有几列是可选的。我正在从外部源读取记录,其中每条记录可以指定可选列的值或不指定。对于每条记录,我想在数据库中插入一行,其中包含给定值以及未指定任何列的列默认值。

如果指定了所有列,我显然只使用基本的INSERT语句:

db_cursor.execute("insert into table (col1, col2, col3, col4, col5) " + 
                  "values (%s, %s, %s, %s, %s)",
                  (value_1, value_2, value_3, value_4, value_5))

但是,如果未指定某些值,则似乎不是仅使用默认值的简单方法。您可以在SQL中使用DEFAULT关键字(或者,等效地,将这些列完全保留在insert语句之外),例如。

db_cursor.execute("insert into table (col1, col2, col3, col4, col5) " + 
                  "values (%s, %s, %s, DEFAULT, %s)",
                  (value_1, value_2, value_3, value_5))

但是您无法将'DEFAULT'作为占位符值传递;它会成为那个字符串。

到目前为止,我只能想到解决这个问题的三种方法:

  1. 根据输入数据在运行时构建SQL查询字符串,而不是使用参数化。由于通常的SQL注入原因,这是一种非常强大的反模式。 (此应用程序实际上并不具备安全性,但我不希望在我的代码中出现这种反模式。)

  2. 为指定和未指定参数的每种可能组合写一个不同的查询字符串。这里,如果其中四列是可选的,那么2 ^ 4 = 16个不同的命令运行相同的查询。这显然是行不通的。

  3. 使应用程序知道默认值,并在未指定列的情况下明确发送它们。这打破了SPOT的默认值,所有参与的维护和互操作性问题(多个应用程序读取数据库)。在我能想到的方法中,这可能是最不好的,但我仍然不愿意这样做。

  4. 是否有更简单的方法来管理动态发送默认值?

1 个答案:

答案 0 :(得分:1)

我通常处理此问题的方法是使用占位符代替列列表,并使用字符串format()列出列。这是安全的,因为列列表由dev控制,并且不是不受信任的用户输入。

stmt_without_col_names = 'INSERT INTO table ({}) VALUES ({})'
input_values = [1, None, 1, None, None]
columns = ('col1', 'col2', 'col3', 'col4', 'col5')
columns_to_keep = {k: v for k, v in zip(columns, input_values) if v is not None}
# note: relies on dict key ordering remaining the same
# this is true if the dict is not modified *at all* between creation
# and the statement execution - use an OrderedDict or other data  
# structure instead if you're worried
format_str = ','.join(['%s'] * len(columns_to_keep))
stmt = stmt_without_col_names.format(columns_to_keep.keys(), format_str)
# stmt looks like "INSERT INTO table (['col3', 'col1']) VALUES (%s,%s)"
cursor.execute(stmt, columns_to_keep.values())
相关问题