【问题标题】:psycopg2 insert python dictionary as jsonpsycopg2 将 python 字典插入为 json
【发布时间】:2015-08-03 20:37:03
【问题描述】:

我想将 python 字典作为 json 插入我的 postgresql 数据库(通过 python 和 psycopg2)。

我有:

thedictionary = {'price money': '$1', 'name': 'Google', 'color': '', 'imgurl': 'http://www.google.com/images/nav_logo225.png', 'charateristics': 'No Description', 'store': 'google'}

cur.execute("INSERT INTO product(store_id, url, price, charecteristics, color, dimensions) VALUES (%d, %s, %s, %d, %s, %s)", (1,  'http://www.google.com', '$20', thedictionary, 'red', '8.5x11'))

它给出了错误信息:

cur.execute("INSERT INTO product(store_id, url, price, charecteristics, color, dimensions) VALUES (%d, %s, %s, %d, %s, %s)", (1, ' http://www.google.com', '$20', thedictionary, 'red', '8.5x11')) psycopg2.ProgrammingError: can't adapt type 'dict'

我不知道如何从这里开始。 我在互联网上找不到任何关于如何做这种确切的事情的信息,而且我对 psycopg2 很陌生。

【问题讨论】:

  • @bibangamba 是使用最新 postgres 版本的人应该查看的链接吗?
  • 我觉得没问题。我正在使用 postgres 10,它没有任何问题
  • @Felipe Augusto 有最好的答案,谢谢!

标签: python postgresql dictionary psycopg2


【解决方案1】:
cur.execute("INSERT INTO product(store_id, url, price, charecteristics, color, dimensions) VALUES (%s, %s, %s, %s, %s, %s)", (1,  'http://www.google.com', '$20', json.dumps(thedictionary), 'red', '8.5x11'))

这将解决您的问题。但是,您确实应该将键和值存储在它们自己单独的列中。要检索字典,请执行以下操作:

cur.execute('select charecteristics from product where store_id = 1')
dictionary = json.loads(cur.fetchone()[0])

【讨论】:

  • 如果您想避免对所有 %s 占位符进行硬编码,您也可以在以列表形式获取列后使用 ', '.join(["%s"] * len(columns)),如下所示:columns = list(dictionary.keys())
【解决方案2】:

来自psycopg docs

注意您可以使用 register_adapter() 将任何 Python 字典适配为 JSON,注册 Json 或任何子类或工厂创建兼容适配器:

psycopg2.extensions.register_adapter(dict, psycopg2.extras.Json)

虽然这个设置是全局的,所以它与类似的适配器不兼容,例如由 register_hstore() 注册的适配器。 JSON 支持的任何其他对象都可以以相同的方式注册,但这会破坏默认的适配规则,所以要小心不必要的副作用。

所以,就我而言,我所做的是:

from psycopg2.extensions import register_adapter

register_adapter(dict, Json)

它就像一个魅力。

【讨论】:

    【解决方案3】:

    从 psycopg2 2.5 版开始,您可以使用 Json 适配器。

    Psycopg 可以使 Python 对象适应 PostgreSQL json 和 jsonb 类型。对于 PostgreSQL 9.2 及以下版本,开箱即用。

    from psycopg2.extras import Json
    curs.execute("insert into mytable (jsondata) values (%s)", [ Json({'a': 100}) ] )
    

    有关更多信息,请参阅文档: https://www.psycopg.org/docs/extras.html#json-adaptation

    【讨论】:

      【解决方案4】:

      您可以使用psycopg2.extras.Json 将dict 转换为postgre 接受的json。

      from psycopg2.extras import Json
      
      thedictionary = {'price money': '$1', 
      'name': 'Google', 'color': '', 'imgurl': 'http://www.google.com/images/nav_logo225.png', 'charateristics': 'No Description', 'store': 'google'}
      
      item ={
          "store_id":1,
          "url": 'http://www.google.com', 
          "price":'$20', 
          "charecteristics":Json(thedictionary), 
          "color":'red', 
          "dimensions":'8.5x11'
      }
      
      def sql_insert(tableName, data_dict):
          '''
          INSERT INTO product (store_id,  url,  price,  charecteristics,  color,  dimensions)
          VALUES (%(store_id)s, %(url)s, %(price)s, %(charecteristics)s, %(color)s, %(dimensions)s );
          '''
          sql = '''
              INSERT INTO %s (%s)
              VALUES (%%(%s)s );
              '''   % (tableName, ',  '.join(data_dict),  ')s, %('.join(data_dict))
          return sql
      
      tableName = 'product'
      sql = sql_insert(tableName, item)
      
      cur.execute(sql, item)
      

      更多信息,您可以查看the official document

      class psycopg2.extras.Json(adapted, dumps=None)
      
          An ISQLQuote wrapper to adapt a Python object to json data type.
      
          Json can be used to wrap any object supported by the provided dumps function. If none is provided, the standard json.dumps() is used (simplejson for Python < 2.6; getquoted() will raise ImportError if the module is not available).
      
          dumps(obj)
          Serialize obj in JSON format.
      
          The default is to call json.dumps() or the dumps function provided in the constructor. You can override this method to create a customized JSON wrapper.
      

      【讨论】:

      • 显然,这不适用于 INT 数据类型。我试过这个,但我得到一个错误: InvalidTextRepresentation: invalid input syntax for type integer: "" LINE 3: VALUES ('', 'Verlängerung',...) 有什么想法吗?
      • @Nofy 你可以参考我上一篇文章。
      • 同理,成功插入特征。现在如何从产品中选择特性并打印所有 imgurl。我的专栏是文本类型。当我从产品中运行选择特性时,将结果作为字符串而不是字典。如何获取 dict 并获取每个对象。
      • 如果格式为 json 风格,可以使用select charecteristics::json from product。或在 python 中,使用json.loads(json str) 或eval(dict str)。
      【解决方案5】:

      首先,错误意味着您试图将 dict 值推送到无法接受它的列类型(TEXT 等..)

      接受的解决方案是正确的,将其从 JSON/dict -> 字符串转换为存储它。

      但是,有一种列类型可以接受它:JSON

      我建议首先创建一个 JSON 字段以保留类似字典的对象。原因是:

      1. 您可以简单地将字典按原样推送到数据库,无需 json.dumps 或其他转换(因为请记住,当您推送时 - 您需要 json.dumps 但当您读取它时python 稍后你需要json.loads(从字符串转换回来-> dict)。
      2. 您可以在真正的 JSON 列中查询其内容,这是字符串时无法做到的。

      https://www.postgresqltutorial.com/postgresql-json/

      因此,在创建列时,我建议将 {} 与 NULL 设为默认值:

      CREATE TABLE my_table (
         ...
         my_json_col JSON default '{}'::JSON
         ...
      )
      

      【讨论】:

        【解决方案6】:

        只需将dict类型转换为json_str,使用json.dumps(adict)即可。

        import pandas as pd
        import json
        import psycopg2
        from sqlalchemy import create_engine
        engine_nf = create_engine('postgresql+psycopg2://user:password@192.168.100.120:5432/database')
        sql_read = lambda sql: pd.read_sql(sql, engine_nf)
        sql_execute = lambda sql: pd.io.sql.execute(sql, engine_nf)
        
        sql = '''
        CREATE TABLE if not exists product (
            store_id  int
            , url  text
            , price text
            , charecteristics json
            , color text
            , dimensions text
        )
        '''
        _ = sql_execute(sql)
        
        thedictionary = {'price money': '$1', 'name': 'Google', 
            'color': '', 'imgurl': 'http://www.google.com/images/nav_logo225.png', 
            'charateristics': 'No Description', 
            'store': 'google'}
        
        sql = '''
        INSERT INTO product(store_id, url, price, charecteristics, color, dimensions) 
        VALUES (%d, '%s', '%s', '%s', '%s', '%s')
        ''' % (1, 'http://www.google.com', '$20', 
               json.dumps(thedictionary), 'red', '8.5x11')
        
        sql_execute(sql)
        
        sql = '''
        select * 
        from product
        '''
        df = sql_read(sql)
        df
            #   store_id    url price   charecteristics color   dimensions
            # 0 1   http://www.google.com   $20 {'price money': '$1', 'name': 'Google', 'color...   red 8.5x11
        
        charecteristics = df['charecteristics'].iloc[0]
        type(charecteristics)
            # dict
        

        事实上,我喜欢另一种将数据转储到 postgres 的方式。

        import io
        import csv
        def df2db(df_a, table_name, engine):
            output = io.StringIO()
            # ignore the index
            df_a.to_csv(output, sep='\t', index = False, header = False, quoting=csv.QUOTE_NONE)
            output.getvalue()
            # jump to start of stream
            output.seek(0)
        
            #engine ---- from sqlalchemy import create_engine
            connection = engine.raw_connection() 
            cursor = connection.cursor()
            # null value become ''
            cursor.copy_from(output,table_name,null='')
            connection.commit()
            cursor.close()
        
        
        df = sql_read('select * from product')
        type(df.charecteristics.iloc[0])
        df.charecteristics = df.charecteristics.map(json.dumps)
        
        # dump pandas DataFrame to postgres
        df2db(df, 'product', engine_nf)
        df_end = sql_read('select * from product')
        

        【讨论】:

          【解决方案7】:

          您希望将每个键作为自己的列是否有特殊原因? Postgres 允许您在包含有效 JSON 或 JSONB 的单个列中执行直接查询操作

          这意味着您可以简单地创建一个带有 ID(主键)和元数据的 2 列数据库,然后执行以下查询:

          SELECT * FROM users WHERE metadata @> '{"key": "value"}';
          

          Here 对您来说是一个很好的资源。

          【讨论】:

            猜你喜欢
            • 2015-06-10
            • 1970-01-01
            • 2022-10-18
            • 2022-01-21
            • 2017-08-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多