2015-04-05 2 views
12

Каков наилучший способ вставить словарь python со многими ключами в базу данных Postgres без необходимости перечислять все ключи?Вставить словарь Python с помощью Psycopg2

Я хотел бы сделать что-то вроде ...

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

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

ответ

6

Что-то вдоль этих линий должны сделать это:

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

cols = songs.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 Injection).

Другой вариант, прохождения dict к execute, будет использовать эти строки вместо vals, vals_str_list и vals_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) 
+1

Я бы также заменил 'cols' на' [cursor.mogrify (x) для x в cols] 'и тот же для' vals_str', чтобы помешать SQL-инъекциям. – 9000

+0

Согласился, что это, безусловно, добавит дополнительной защиты. – khampson

+0

Чтение документа psycopg2 немного, 'mogrify' может быть ненужным, так как определение этого метода говорит о том, что ** Возвращенная строка является именно той, которая будет отправлена ​​в базу данных, где выполняется метод execute() или аналогичный. ** , поэтому я думаю, что столбцы и строки '% s' будут« mogrify »во время вызова' execute'. – khampson

14
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 адаптирует a tuple - record и AsIs d oes, что бы было сделано с помощью замены строки Python.

+0

Не знал о 'AsIs'. Интересно, что мне нужно иметь дело с размноженным '% s' ... – khampson

+1

Я был обеспокоен тем, что это может оставить уязвимым для инъекций пользователя из-за недостаточного экранирования, но - по крайней мере, используя базовый пример - появляются одиночные апострофы чтобы быть надлежащим образом экранированным. У меня нет времени для тестирования более совершенных методов инъекций, таких как (или аналогичных) тем, которые описаны в следующей ссылке, поэтому они все еще могут быть предметом озабоченности по сравнению с более стандартными методами параметризации. http://stackoverflow.com/a/12118602/2540707 –

9

Вы также можете вставить несколько строк, используя 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

1

другой подход для запроса к MySQL или PGSQL из словаря использует конструкцию %(dic_key)s, оно будет заменено на значение из словаря coresponding по dic_key как {'dic_key': 'dic value'} работает идеально и предотвратить sqlInjection испытываться: Python 2.7 смотрите ниже:

 
# in_dict = {u'report_range': None, u'report_description': None, 'user_id': 6, u'rtype': None, u'datapool_id': 1, u'report_name': u'test suka 1', u'category_id': 3, u'report_id': None} 


cursor.execute('INSERT INTO report_template (report_id, report_name, report_description, report_range, datapool_id, category_id, rtype, user_id) VALUES ' \ 
                   '(DEFAULT, %(report_name)s, %(report_description)s, %(report_range)s, %(datapool_id)s, %(category_id)s, %(rtype)s, %(user_id)s) ' \ 
                   'RETURNING "report_id";', in_dict) 


OUT: 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";

1

Новый модуль 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", "бар", "Баз") значения (% 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", "бар", "Баз") значения (% (Foo) с,% (бар) с,% (БАЗ) с)

Хотя конкатенация приведет к тому же результату, что не следует использовать для этой цели, в соответствии с psycopg2 документации:

Предупреждение: Никогда, никогда, НИКОГДА не использование Python конкатенация строк (+) или интерполяция параметров строк (%) для передачи переменных в строку запроса SQL. Даже под дулом пистолета.

Смежные вопросы