¿Cómo usar variables en la declaración SQL en Python?

92

Ok, no tengo tanta experiencia en Python.

Tengo el siguiente código de Python:

cursor.execute("INSERT INTO table VALUES var1, var2, var3,")

donde var1es un número entero var2y var3son cadenas.

¿Cómo puedo escribir los nombres de las variables sin que Python los incluya como parte del texto de la consulta?

usuario111606
fuente

Respuestas:

103
cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

Tenga en cuenta que los parámetros se pasan como una tupla.

La API de la base de datos realiza el escape y la cita de variables correctamente. Tenga cuidado de no utilizar el operador de formato de cadena ( %), porque

  1. no se escapa ni cita.
  2. es propenso a ataques de formato de cadena no controlados, por ejemplo, inyección SQL .
Ayman Hourieh
fuente
Interesante, ¿por qué funciona con las vars por separado en lugar de en una matriz (var1, var2, var3)?
Andomar
De acuerdo con las especificaciones de la API de DB, parece que puede ser de cualquier manera: python.org/dev/peps/pep-0249
Ayman Hourieh
9
@thekashyap Vuelve a leer con atención. Lo que es inseguro es usar el operador de formato de cadena %. De hecho, lo digo en la respuesta.
Ayman Hourieh
mi mal ... imaginé un en % lugar de ,entre la cadena y las variables ... no puedo deshacer mi voto en contra debido a varias razones ... personalmente me gustaría ver palabras como inseguro / ataque, etc.en la descripción donde dices don 't use %..
Kashyap
1
@eric la respuesta dice que no use el % operador para formatear la cadena. Los que están %en la cadena están siendo utilizados cursor.executedirectamente y, dado que sabe que está generando SQL, puede hacer más para protegerlo.
Mark Ransom
68

Se permiten diferentes implementaciones de Python DB-API para usar diferentes marcadores de posición, por lo que deberá averiguar cuál está usando, podría ser (por ejemplo, con MySQLdb):

cursor.execute("INSERT INTO table VALUES (%s, %s, %s)", (var1, var2, var3))

o (por ejemplo, con sqlite3 de la biblioteca estándar de Python):

cursor.execute("INSERT INTO table VALUES (?, ?, ?)", (var1, var2, var3))

u otros aún (después de VALUESque podría tener (:1, :2, :3), o "estilos con nombre" (:fee, :fie, :fo)o (%(fee)s, %(fie)s, %(fo)s)donde pasa un dict en lugar de un mapa como segundo argumento execute). Verifique la paramstyleconstante de cadena en el módulo DB API que está utilizando y busque paramstyle en http://www.python.org/dev/peps/pep-0249/ para ver cuáles son todos los estilos de paso de parámetros.

Alex Martelli
fuente
¿Es posible hacer lo mismo pero con el script SQL externo?
Novitoll
47

Muchas maneras. NO use el más obvio ( %scon %) en código real, está abierto a ataques .

Aquí copiar y pegar desde pydoc de sqlite3 :

# Never do this -- insecure!
symbol = 'RHAT'
c.execute("SELECT * FROM stocks WHERE symbol = '%s'" % symbol)

# Do this instead
t = ('RHAT',)
c.execute('SELECT * FROM stocks WHERE symbol=?', t)
print c.fetchone()

# Larger example that inserts many records at a time
purchases = [('2006-03-28', 'BUY', 'IBM', 1000, 45.00),
             ('2006-04-05', 'BUY', 'MSFT', 1000, 72.00),
             ('2006-04-06', 'SELL', 'IBM', 500, 53.00),
            ]
c.executemany('INSERT INTO stocks VALUES (?,?,?,?,?)', purchases)

Más ejemplos si lo necesita:

# Multiple values single statement/execution
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', ('RHAT', 'MSO'))
print c.fetchall()
c.execute('SELECT * FROM stocks WHERE symbol IN (?, ?)', ('RHAT', 'MSO'))
print c.fetchall()
# This also works, though ones above are better as a habit as it's inline with syntax of executemany().. but your choice.
c.execute('SELECT * FROM stocks WHERE symbol=? OR symbol=?', 'RHAT', 'MSO')
print c.fetchall()
# Insert a single item
c.execute('INSERT INTO stocks VALUES (?,?,?,?,?)', ('2006-03-28', 'BUY', 'IBM', 1000, 45.00))
Kashyap
fuente
4
Algunas de las implementaciones de DB-API utilizan% s para sus variables, sobre todo psycopg2 para PostgreSQL. Esto no debe confundirse (aunque es fácil) con el uso de% s con el operador% para el reemplazo de cadenas. Sería realmente bueno si, para la portabilidad, pudiéramos tener una forma estándar definida de especificar parámetros SQL para DB-API.
ThatAintWorking
25

http://www.amk.ca/python/writing/DB-API.html

Tenga cuidado cuando simplemente agregue valores de variables a sus declaraciones: Imagine un usuario nombrándose a sí mismo ';DROP TABLE Users;'- Es por eso que necesita usar el escape sql, que Python le proporciona cuando usa el cursor.execute de manera decente. Ejemplo en la URL es:

cursor.execute("insert into Attendees values (?, ?, ?)", (name,
seminar, paid) )
Num lock
fuente
13
En realidad, no es un escape SQL. Es un enlace variable, que es mucho más simple y directo. Los valores se enlazan en la instrucción SQL después del análisis, haciéndolo inmune a cualquier ataque de inyección.
S.Lott
bueno, si se trata de un escape de SQL o un enlace de variables depende de qué tan bueno o malo sea su servidor de base de datos / controlador DB-API. He visto algunas bases de datos de producción del mundo real, ampliamente implementadas, que tienen su controlador DB-API simplemente escapando, en lugar de mantener los datos y el código fuera de banda en el cable. No hace falta decir que no tengo mucho respeto por las llamadas "bases de datos".
Charles Duffy