Как явно указать строковое значение (Python DB API/Psycopg2)
По некоторым причинам я хотел бы сделать явное цитирование строкового значения (став частью составного запроса SQL) вместо ожидания неявного цитирования, выполняемого cursor.execute
метод по содержанию его второго параметра.
Под "неявной цитатой" я имею в виду:
value = "Unsafe string"
query = "SELECT * FROM some_table WHERE some_char_field = %s;"
cursor.execute( query, (value,) ) # value will be correctly quoted
Я бы предпочел что-то подобное:
value = "Unsafe string"
query = "SELECT * FROM some_table WHERE some_char_field = %s;" % \
READY_TO_USE_QUOTING_FUNCTION(value)
cursor.execute( query ) # value will be correctly quoted, too
Такой низкий уровень READY_TO_USE_QUOTING_FUNCTION
ожидается спецификацией Python DB API (я не смог найти такую функциональность в документе PEP 249). Если нет, может быть, Psycopg2 обеспечивает такую функцию? Если нет, может, Django предоставляет такую функцию? Я бы предпочел не писать такую функцию сам...
10 ответов
Итак, мне было любопытно, я пошел и посмотрел на источник psycopg2. Оказывается, мне не нужно было идти дальше, чем в папке с примерами:)
И да, это специфично для psycopg2. По сути, если вы просто хотите заключить строку в кавычки, вы сделаете это:
from psycopg2.extensions import adapt
print adapt("Hello World'; DROP DATABASE World;")
Но то, что вы, вероятно, хотите сделать, это написать и зарегистрировать свой собственный адаптер;
В папке с примерами psycopg2 вы найдете файл "myfirstrecipe.py", в котором есть пример того, как особым образом приводить и цитировать определенный тип.
Если у вас есть объекты для того, что вы хотите сделать, вы можете просто создать адаптер, соответствующий протоколу "IPsycopgSQLQuote" (см. Pydocs для примера myfirstrecipe.py... на самом деле это единственная ссылка, которую я могу найти на это имя) который цитирует ваш объект и затем регистрирует его следующим образом:
from psycopg2.extensions import register_adapter
register_adapter(mytype, myadapter)
Кроме того, другие примеры интересны; особенно 'dialtone.py' и 'simple.py'.
Я полагаю, вы ищете функцию mogrify.
Пример:
>>> cur.mogrify("INSERT INTO test (num, data) VALUES (%s, %s)", (42, 'bar'))
"INSERT INTO test (num, data) VALUES (42, E'bar')"
Вы должны стараться не делать свои собственные цитаты. Как указывали люди, это будет не только специфично для БД, но и недостатки в цитировании являются источником ошибок SQL-инъекций.
Если вы не хотите передавать запросы и значения отдельно, тогда передайте список параметров:
def make_my_query():
# ...
return sql, (value1, value2)
def do_it():
query = make_my_query()
cursor.execute(*query)
(Скорее всего, у меня неправильный синтаксис cursor.execute). Дело в том, что просто, так как cursor.execute принимает несколько аргументов, но это не значит, что вы должны обрабатывать их все по отдельности. Вы можете иметь дело с ними как один список.
Ваш фрагмент кода будет выглядеть следующим образом, в соответствии с документами расширения psycopg
from psycopg2.extensions import adapt
value = "Unsafe string"
query = "SELECT * FROM some_table WHERE some_char_field = %s;" % \
adapt(value).getquoted()
cursor.execute( query ) # value will be correctly quoted, too
getquoted
функция возвращает value
как строка в кавычках и экранированная, так что вы также можете перейти: "SELECT * FROM some_table WHERE some_char_field = " + adapt(value).getquoted()
,
Я не думаю, что вы приводите достаточные аргументы в пользу того, что вы избегаете делать правильный путь. Пожалуйста, используйте APi, как он разработан, и не старайтесь изо всех сил, чтобы сделать ваш код менее читабельным для следующего парня и более хрупким.
Это будет зависеть от базы данных (iirc, MySQL позволяет \
как экранирующий символ, в то время как оракул ожидает, что кавычки будут удвоены: 'my '' quoted string'
).
Кто-то исправит меня, если я ошибаюсь, но метод двойных кавычек является стандартным методом.
Возможно, стоит посмотреть, что делают другие библиотеки абстракции БД (sqlalchemy, cx_Oracle, sqlite и т. Д.).
Я должен спросить - почему вы хотите вставить значения вместо того, чтобы связать их?
Это будет зависеть от БД. В случае MySQLdb, например, connection
класс имеет literal
метод, который преобразует значение в правильное экранированное представление для передачи в MySQL (вот что cursor.execute
использует).
Я полагаю, что в Postgres есть нечто подобное, но я не думаю, что есть функция для экранирования значений как часть спецификации DB API 2.0.
PyPika - еще один хороший вариант для построения операторов SQL. Пример использования (на основе примера на домашней странице проекта):
>>> from pypika import Order, Query
>>> Query.from_('customers').select('id', 'fname', 'lname', 'phone').orderby('id', order=Order.desc)
SELECT "id","fname","lname","phone" FROM "customers" ORDER BY "id" DESC
import re
def db_quote(s):
return "\"" + re.escape(s) + "\""
может сделать работу простого цитирования, который работает по крайней мере с MySQL. Что нам действительно нужно, так это функция cursor.format(), которая будет работать как cursor.execute(), за исключением того, что она будет возвращать результирующий запрос вместо его выполнения. Есть моменты, когда вы не хотите, чтобы запрос еще выполнялся - например, вы можете сначала зарегистрировать его или распечатать для отладки, прежде чем продолжить.
Если вы используете django, вы можете использовать функцию цитирования, которая автоматически адаптируется к текущей настроенной СУБД:
from django.db import backend
my_quoted_variable = backend.DatabaseOperations().quote_name(myvar)