Параметризованные запросы с помощью psycopg2 / Python DB-API и PostgreSQL

Как лучше всего заставить psycopg2 передавать параметризованные запросы в PostgreSQL? Я не хочу писать свои собственные механизмы или адаптеры escpaing, а исходный код psycopg2 и примеры трудно читать в веб-браузере.

Если мне нужно переключиться на что-то вроде PyGreSQL или другого адаптера python pg, меня это устраивает. Мне просто нужна простая параметризация.


person jeffcook2150    schedule 23.09.2009    source источник
comment
Какая параметризация вам нужна? Пример псевдокода будет полезен.   -  person whatnick    schedule 23.09.2009
comment
Замечание, вы можете захотеть изучить SQLAlchemy, стоимость входа может быть немного выше, но это действительно очень хорошая ORM.   -  person Bryan McLemore    schedule 09.11.2009
comment
Для дальнейшего использования ответ находится на первой странице документации: initd.org/psycopg/docs /usage.html   -  person piro    schedule 16.05.2011
comment
По документации примеры очень простые. Нет ничего, что показало бы, как более сложный запрос, такой как обновление, будет выполняться для динамических значений. Что-то вроде: set height=5, weight=70   -  person Mutai Mwiti    schedule 25.07.2018
comment
Stack Overflow становится намного более полезным, если вы ставите конкретную проблему, которую пытаетесь решить. Например, запрос или обновление таблицы и т. Д. В результате ответы здесь являются довольно общими и не помогают решить проблему, с которой я сталкиваюсь, несмотря на название, которое привело меня.   -  person Bill Gale    schedule 09.05.2020


Ответы (3)


psycopg2 следует правилам DB-API 2.0 (изложенным в PEP-249 ). Это означает, что вы можете вызвать execute метод из своего cursor объекта и использовать pyformat стиль привязки, и он выполнит экранирование за вас. Например, следующее должно быть безопасным (и работать):

cursor.execute("SELECT * FROM student WHERE last_name = %(lname)s", 
               {"lname": "Robert'); DROP TABLE students;--"})
person Hank Gay    schedule 24.09.2009
comment
@ mascot6699 нет, потому что запрос параметризован. - person Nathan Hinchey; 02.06.2017
comment
Хорошая новость заключается в том, что, поскольку таблица называется "студент", а не "студенты"; даже если бы код был небезопасным, он бы потерпел неудачу. Урок для черных шляп: проверьте свои подвиги! - person tekHedd; 23.06.2020

Из документации psycopg

(http://initd.org/psycopg/docs/usage.html)

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

Правильный способ передачи переменных в команде SQL - использовать второй аргумент метода execute ():

SQL = "INSERT INTO authors (name) VALUES (%s);" # Note: no quotes

data = ("O'Reilly", )

cur.execute(SQL, data) # Note: no % operator

person Fábio Dias    schedule 23.09.2015

Вот несколько примеров, которые могут оказаться полезными

cursor.execute('SELECT * from table where id = %(some_id)d', {'some_id': 1234})

Или вы можете динамически построить свой запрос на основе имени поля, значения:

fields = ', '.join(my_dict.keys())
values = ', '.join(['%%(%s)s' % x for x in my_dict])
query = 'INSERT INTO some_table (%s) VALUES (%s)' % (fields, values)
cursor.execute(query, my_dict)

Примечание: поля должны быть определены в вашем коде, а не вводимых пользователем данных, иначе вы будете подвержены SQL-инъекции.

person adam    schedule 23.09.2009
comment
Ну, если вы не знаете, что делаете, вам не следует просто объединять ввод в sql-запросы, поскольку это SQL-инъекция. - person jb.; 25.01.2013
comment
отклонено из-за предложения, которое оставляет вас открытыми для SQL-инъекции - person Randy Syring; 23.12.2014
comment
@RandySyring Это доступно для SQL-инъекции, только если ключи не определены должным образом и не имеют правильных идентификаторов. Значения по-прежнему правильно параметризованы. - person All Workers Are Essential; 07.04.2015
comment
То есть, в общем случае, это оставляет вас открытыми для SQL-инъекций. - person rspeer; 22.05.2018
comment
Ваш первый пример не работает. Из документов: The variables placeholder must always be a %s, even if a different placeholder (such as a %d for integers or %f for floats) may look more appropriate. Вы использовали %d. - person Alex Totheroh; 09.09.2020