我正在寻找一种实现交替SQL查询的方法 - 即允许我根据不同列过滤条目的功能。请看以下示例:
el=[["a","b",1],["a","b",3]]
def save_sql(foo):
with sqlite3.connect("fn.db") as db:
cur=db.cursor()
cur.execute("CREATE TABLE IF NOT EXISTS et"
"(var1 VARCHAR, var2 VARCHAR, var3 INT)")
cur.executemany("INSERT INTO et VALUES "
"(?,?,?)", foo)
db.commit()
def load_sql(v1,v2,v3):
with sqlite3.connect("fn.db") as db:
cur=db.cursor()
cur.execute("SELECT * FROM et WHERE var1=? AND var2=? AND var3=?", (v1,v2,v3))
return cur.fetchall()
save_sql(el)
现在,如果我使用load_sql("a","b",1)
,它会起作用。但是假设我只想查询第一列和第三列,即load_sql("a",None,1)
(None
仅用作占位符)或仅查询最后一列load_sql(None,None,5)
,这不起作用。
当然可以使用if语句检查函数调用中提供的变量,但是在列数较大的表中,这可能会变得混乱。
有没有好办法呢?
答案 0 :(得分:2)
如果load_sql()
接受任意数量的关键字参数,其中关键字参数名称将对应于列名称,该怎么办?这些方面的东西:
def load_sql(**values):
with sqlite3.connect("fn.db") as db:
cur = db.cursor()
query = "SELECT * FROM et"
conditions = [f"{column_name} = :{column_name}" for column_name in values]
if conditions:
query = query + " WHERE " + " AND ".join(conditions)
cur.execute(query, values)
return cur.fetchall()
请注意,这里我们相信关键字参数名称是有效的和现有列名(并将它们字符串格式化为查询),这可能会被用作SQL注入攻击向量。
作为旁注,我不能停下来,但认为这对于实际的ORM来说就像是一个重新发明的步骤。查看Python和数据库之间的轻量级PonyORM
或Peewee抽象层。
答案 1 :(得分:2)
如果你希望你的SQL语句保持消毒/安全,它将不可避免地变得混乱,但只要你控制你的功能签名,它就可以保持相当安全,例如:
def load_sql(var1, var2, var3):
fields = dict(field for field in locals().items() if field[1] is not None)
query = "SELECT * FROM et"
if fields: # if at least one field is not None:
query += " WHERE " + " AND ".join((k + "=?" for k in fields.keys()))
with sqlite3.connect("fn.db") as db:
cur = db.cursor()
cur.execute(query, fields.values())
return cur.fetchall()
您可以使用load_sql(**kwargs)
替换函数签名,然后使用kwargs.items()
代替locals.items()
,以便您可以传递任意列名,但这可能非常危险,当然不建议使用