【问题标题】:Python SQLite insert with dynamic columns or use DEFAULT valuePython SQLite 插入动态列或使用 DEFAULT 值
【发布时间】:2022-01-17 16:45:35
【问题描述】:

我正在使用flask-restful,用户可以发布一些必需的和一些可选的数据,我想用这些数据创建一个SQLite 插入语句。问题是我不想通过使用变量作为列名并检查哪个参数是None 以及哪个有一些数据来破坏 sql 语句。我希望我可以只使用所有列,当 arg2 或 arg3 为None 时,数据库将使用列默认值。可悲的是,我收到了NOT NULL constraint failed: mytable.col1 的消息。使用我的方法从表创建中删除 NOT NULL 只会在使用我的语句时插入 NULL。

是否有明确的方式告诉 SQLite 使用其默认值?

如果没有,是否有更优雅的方式来获取默认值而不是执行某些表模式查询?

如果没有并且不存在其他解决方案,那么屠宰 sql 语句的最有效方法是什么?

parser_post = reqparse.RequestParser()
parser_post.add_argument('arg1', type=str, required=True)
parser_post.add_argument('arg2', type=str)
parser_post.add_argument('arg3', type=str)
args = parser_post.parse_args()

# ... db init code

cur.execute("insert into mytable (col1, col2, col3) values (?, ?, ?)", 
(args['arg1'], args['arg2'], args['arg3']))

表是这样创建的:

sql = """CREATE TABLE mytable (
    PK_id       INTEGER PRIMARY KEY AUTOINCREMENT,
    col1        INTEGER NOT NULL,
    col2        TEXT DEFAULT "global" NOT NULL,
    col3        TEXT DEFAULT "global" NOT NULL)"""
cursor.execute(sql)

【问题讨论】:

  • 为什么不创建一个包含“args”条目以及缺失条目的默认值的字典?然后可以将此字典用作参数化 SQL 语句的基础。
  • @MichaelButscher 可以工作,但它在客户端代码中重复数据库端值:容易不同步。
  • @MichaelButscher 加上默认值?这意味着我需要以某种方式从数据库表信息中查询它们(因为硬编码它们是不行的),这也不是最优的。

标签: python sql python-3.x database sqlite


【解决方案1】:

使用数据库端默认值的一种方法是不为插入中的特定列提供数据。

在这种情况下,服务器将为该列插入默认值。如果您没有在 CREATE TABLE(或更高版本的 ALTER TABLEs)中指定默认值,则默认值为 NULL(对应于 Python None)。

然而,由于 col2 确实有一个数据库指定的默认值,通过col2 TEXT DEFAULT "global" NOT NULL,那么在插入中不指定col2 将导致sqlite 使用"global"。同上col3

您可以动态构建查询,以排除带有None 的列,只要您小心处理查询字符串中的内容即可。

def prep_qry(args, colnames):
    """this query is secure as long as `colnames` contains trusted data
    standard parametrized query mechanism secures `args`"""

    binds,use = [],[]

    for colname, value in zip(colnames,args):
        if value is not None:
            use.extend([colname,","])
            binds.extend(["?",","])


    parts = ["insert into mytable ("]
    use = use[:-1]
    binds = binds[:-1]
    
    parts.extend(use)
    parts.append(") values(")
    parts.extend(binds)
    parts.append(")")

    qry = " ".join(parts)

    return qry, tuple([v for v in args if not v is None])

print(prep_qry([1,None,3], ["col1", "col2", "col3"]))
print(prep_qry([1,2,3], ["col1", "col2", "col3"]))

输出:

('insert into mytable ( col1 , col3 ) values( ? , ? )', (1, 3))
('insert into mytable ( col1 , col2 , col3 ) values( ? , ? , ? )', (1, 2, 3))

【讨论】:

  • 这应该可行,因为我可以做到prep_qry(args.values(), args.keys()) 对吗?或者这似乎不安全?信息:只有通过parser_post.add_argument() 添加的参数才会出现在args 中。 arg 键是为了简单和可读性的列名
  • 是的,用来自 parser_post.parse_args() 的 colnames= 硬编码的 colnames 和 args 提供这个函数。对于额外的偏执狂,您可以检查各个 colnames 是否与 sql 列命名约定匹配,即 ASCII 中的字母数字和 _,几乎。正则表达式“[a-z0-9_]+”。检查整个 colname 是否合适,而不仅仅是开始。大量 SQL 注入发生在 ' 单引号变量周围。
  • @Slluxx 和...不要忘记接受/投票 IF 它实际上可以帮助您。欢迎登机。
  • 它完美无缺。现在我只需要调整你的脚本来更新和删除语句。但这超出了最初问题的范围。非常感谢!
猜你喜欢
  • 1970-01-01
  • 2016-10-15
  • 2020-02-20
  • 1970-01-01
  • 1970-01-01
  • 2019-03-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多