【问题标题】:psycopg2 execute_values with list of class objects带有类对象列表的 psycopg2 execute_values
【发布时间】:2018-09-29 08:37:27
【问题描述】:

我有一个Personidfnamelname 的课程。我的程序将包含大量 Person 对象,我想将其插入到我的 PostgreSQL 数据库中。

我想通过psycopg2.execute_values 方法来做到这一点,该方法需要为我的班级定义__getitem__

__getitem__ 当前正在返回tuple(id, fname, lname),这导致pscopg2.execute_values 尝试执行:insert into persons(id, fname, lname) values ((1, 'test1', 'test1'))...

import psycopg2.extras


class Person:
    def __init__(self, id, fname, lname):
        self.id = id
        self.fname = fname
        self.lname = lname

    def __len__(self):
        return 1

    def __getitem__(self, item):
        return self.id, self.fname, self.lname


dsn = "user=my_user host=localhost port=5432 dbname=my_db"
query = "insert into persons(id, fname, lname) values %s"

p1 = Person(1, 'test1', 'test1')
p2 = Person(2, 'test2', 'test2')

records = list()
records.append(p1)
records.append(p2)
with psycopg2.connect(dsn=dsn) as conn:
    with conn.cursor() as cursor:
        psycopg2.extras.execute_values(cursor, query, records, template=None, page_size=1000)

psycopg2.ProgrammingError: INSERT has more target columns than expressions
LINE 1: insert into persons(id, fname, lname) values ((1, 'test1', 'test1'))...
                                ^
HINT:  The insertion source is a row expression containing the same number of columns expected by the INSERT. Did you accidentally use extra parentheses?

使用namedtuple 有效:

from collections import namedtuple
import psycopg2.extras


Person = namedtuple('Person', ['id', 'fname', 'lname'])

dsn = "user=my_user host=localhost port=5432 dbname=my_db"
query = "insert into persons(id, fname, lname) values %s"

p1 = Person(1, 'test1', 'test1')
p2 = Person(2, 'test2', 'test2')

records = list()
records.append(p1)
records.append(p2)

with psycopg2.connect(dsn=dsn) as conn:
    with conn.cursor() as cursor:
        psycopg2.extras.execute_values(cursor, query, records, template=None, page_size=1000)

但是,我想实现不同的 __eq____hash__ 实现。如果有办法为namedtuple 覆盖这些方法,那么我愿意采用这种方法。

否则如何修改__getitem__分别返回idfnamelname,使得查询变为:

insert into persons(id, fname, lname) values (1, 'test1', 'test1')...

【问题讨论】:

    标签: python psycopg2


    【解决方案1】:

    定义__conform__ 似乎可以解决问题:https://www.python.org/dev/peps/pep-0246/

    import psycopg2.extras
    from psycopg2.extensions import AsIs, ISQLQuote
    
    
    class Person:
        def __init__(self, id, fname, lname):
            self.id = id
            self.fname = fname
            self.lname = lname
    
        def __len__(self):
            return 1
    
        def __getitem__(self, key):
            return self
    
        def __conform__(self, protocol):
            if protocol is ISQLQuote:
                return AsIs("{id}, '{fname}', '{lname}'".format(id=self.id, fname=self.fname, lname=self.lname))
    
            return None
    
    
    dsn = "user=my_user host=localhost port=5432 dbname=my_db"
    query = "insert into persons(id, fname, lname) values %s"
    
    p1 = Person(1, 'test1', 'test1')
    p2 = Person(2, 'test2', 'test2')
    
    records = list()
    records.append(p1)
    records.append(p2)
    
    with psycopg2.connect(dsn=dsn) as conn:
        with conn.cursor() as cursor:
            psycopg2.extras.execute_values(cursor, query, records, template=None, page_size=1000)
    

    不确定这是否是推荐的方法,所以我会保持打开状态,希望有人能加入。

    【讨论】:

    • 这可能对 SQL 注入开放。 psycopg.org/docs/… 显示了一个为类创建适配器的示例,它们在将所有值传递给AsIs 之前对所有值调用adapt(x).getquoted()。当我有时间自己解决这个问题时,我会发布答案。
    猜你喜欢
    • 2019-02-19
    • 1970-01-01
    • 2021-03-03
    • 2013-10-09
    • 2021-09-28
    • 2021-06-05
    • 2023-03-29
    • 2015-04-08
    • 2016-05-08
    相关资源
    最近更新 更多