【问题标题】:Insert Python Dictionary using Psycopg2使用 Psycopg2 插入 Python 字典
【发布时间】:2015-06-10 07:11:06
【问题描述】:

将具有许多键的 Python 字典插入 Postgres 数据库而不必枚举所有键的最佳方法是什么?

我想做一些类似...

song = dict()
song['title'] = 'song 1'
song['artist'] = 'artist 1'
...

cursor.execute('INSERT INTO song_table (song.keys()) VALUES (song)')

【问题讨论】:

    标签: python postgresql psycopg2


    【解决方案1】:
    from psycopg2.extensions import AsIs
    
    song = {
        'title': 'song 1',
        'artist': 'artist 1'
    }
    
    columns = song.keys()
    values = [song[column] for column in columns]
    
    insert_statement = 'insert into song_table (%s) values %s'
    
        # cursor.execute(insert_statement, (AsIs(','.join(columns)), tuple(values)))
    print cursor.mogrify(insert_statement, (AsIs(','.join(columns)), tuple(values)))
    

    打印:

    insert into song_table (artist,title) values ('artist 1', 'song 1')
    

    Psycopg 将 tuple 适配为 recordAsIs 执行 Python 的字符串替换将完成的工作。

    【讨论】:

    • 不知道AsIs。有趣——省去了处理乘数的%ss...
    • 我担心这可能会使用户由于转义不足而容易受到注入攻击,但是 - 至少使用一个基本示例 - 单个撇号似乎已正确转义。我没有时间测试更高级的注入技术,例如(或类似于)以下链接中描述的技术,因此与更标准的参数化技术相比,它们可能仍然是一个问题。 stackoverflow.com/a/12118602/2540707
    • 为什么不直接使用 song.values() 作为值? :)
    • 如果我想获取插入行的id,那该怎么办?
    • 获取 mogrify 需要一个 psycopg2.extensions.cursor 但收到了 insert_statement 的“str”,这显然是一个字符串。
    【解决方案2】:

    您还可以使用dictionary 插入多行。如果您有以下情况:

    namedict = ({"first_name":"Joshua", "last_name":"Drake"},
                {"first_name":"Steven", "last_name":"Foo"},
                {"first_name":"David", "last_name":"Bar"})
    

    您可以使用以下方法在字典中插入所有三行:

    cur = conn.cursor()
    cur.executemany("""INSERT INTO bar(first_name,last_name) VALUES (%(first_name)s, %(last_name)s)""", namedict)
    

    cur.executemany 语句将自动遍历字典并对每一行执行 INSERT 查询。

    PS:本例取自here

    【讨论】:

    • 嗨 vikas,有没有在循环内与 cursor.execute() 进行性能比较?
    【解决方案3】:

    应该这样做:

    song = dict()
    song['title'] = 'song 1'
    song['artist'] = 'artist 1'
    
    cols=song.keys();
    
    vals = [song[x] for x in cols]
    vals_str_list = ["%s"] * len(vals)
    vals_str = ", ".join(vals_str_list)
    
    cursor.execute("INSERT INTO song_table ({cols}) VALUES ({vals_str})".format(
                   cols = cols, vals_str = vals_str), vals)
    

    关键部分是生成的%s元素字符串,并在format中使用该字符串,并将列表直接传递给execute调用,以便psycopg2可以插入每个项目在vals 列表中(从而防止可能的SQL 注入)。

    另一个变体,将dict 传递给execute,将使用这些行代替上面的valsvals_str_listvals_str

    vals_str2 = ", ".join(["%({0})s".format(x) for x in cols])
    
    cursor.execute("INSERT INTO song_table ({cols}) VALUES ({vals_str})".format(
                   cols = cols, vals_str = vals_str2), song)
    

    【讨论】:

    • 我也会用 [cursor.mogrify(x) for x in cols] 替换 colsvals_str 也一样,以阻止 SQL 注入。
    • 同意这肯定会增加额外的保护。
    • 稍微阅读 psycopg2 文档,mogrify 可能是不必要的,因为该方法的定义表明 返回的字符串正是将发送到运行执行的数据库的字符串() 方法或类似方法。,所以我认为在execute 调用期间,列和%s 字符串将是mogrify'ed。
    • 我认为主要区别在于它是在调用execute 之前还是在execute 本身期间进行的。如果之前完成,它基本上只会在 execute 运行期间返回相同的字符串,因为它已经被 mogrified 了。如果您想确切地知道网络上发生了什么,例如,对于您的日志,提前做可能是有益的。
    【解决方案4】:

    为此目的创建了新的sql 模块,并在 psycopg2 版本 2.7 中添加。根据文档:

    如果您需要动态生成 SQL 查询(例如动态选择表名),您可以使用 psycopg2.sql 模块提供的工具。

    文档中给出了两个示例:http://initd.org/psycopg/docs/sql.html

    names = ['foo', 'bar', 'baz']
    
    q1 = sql.SQL("insert into table ({}) values ({})").format(
        sql.SQL(', ').join(map(sql.Identifier, names)),
        sql.SQL(', ').join(sql.Placeholder() * len(names)))
    print(q1.as_string(conn))
    

    插入表(“foo”、“bar”、“baz”)值(%s、%s、%s)

    q2 = sql.SQL("insert into table ({}) values ({})").format(
        sql.SQL(', ').join(map(sql.Identifier, names)),
        sql.SQL(', ').join(map(sql.Placeholder, names)))
    print(q2.as_string(conn))
    

    插入表(“foo”、“bar”、“baz”)值(%(foo)s、%(bar)s、%(baz)s)

    根据 psycopg2 文档,虽然字符串连接会产生相同的结果,但不应将其用于此目的:

    警告:从不、neverNEVER 使用 Python 字符串连接 (+) 或字符串参数插值 (%)将变量传递给 SQL 查询字符串。甚至在枪口下也没有。

    【讨论】:

      【解决方案5】:

      从字典查询 mySQL 或 pgSQL 的另一种方法是使用构造 %(dic_key)s,它将被字典中的值替换为 dic_key 对应的值,例如 {'dic_key': 'dic value'} 工作完美,并防止 sqlInjection 测试:Python 2.7 见下文:

      # in_dict = {u'report_range':无,u'report_description':无,'user_id':6,u'rtype':无,u'datapool_id':1,u'report_name':u'test suka 1',你'category_id':3,你'report_id':无} cursor.execute('INSERT INTO report_template (report_id, report_name, report_description, report_range, datapool_id, category_id, rtype, user_id) VALUES ' \ '(默认值,%(report_name)s,%(report_description)s,%(report_range)s,%(datapool_id)s,%(category_id)s,%(rtype)s,%(user_id)s)' \ '返回“report_id”;', in_dict)


      出去: INSERT INTO report_template (report_id, report_name, report_description, report_range, datapool_id, category_id, rtype, user_id) VALUES (DEFAULT, E'test suka 1', NULL, NULL, 1, 3, NULL, 6) RETURNING "report_id";

      【讨论】:

        【解决方案6】:

        Python 具有某些内置功能,例如 joinlist 使用它们可以生成查询。此外,python 字典提供 keys()values() 可用于分别提取列名和列值。 这是我使用的方法,应该可行。

        song = dict()
        song['title'] = 'song 1'
        song['artist'] = 'artist 1'
        
        query = '''insert into song_table (''' +','.join(list(song.keys()))+''') values '''+ str(tuple(song.values()))
        cursor.execute(query)
        

        【讨论】:

        • 这是一个非常非常糟糕的主意。引用 psycopg 文档:“永远,永远,永远不要使用 Python 字符串连接 (+) 或字符串参数插值 (%) 将变量传递给 SQL 查询字符串。即使是在枪口下。”
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-01-25
        • 1970-01-01
        • 2021-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多