[英]SQLAlchemy : how can I execute a raw INSERT sql query in a Postgres database?
I'm building an app using Python and the clean architecture principles, with TDD.我正在使用 Python 和简洁的架构原则以及 TDD 构建一个应用程序。
Some unit tests require executing some raw SQL queries against an in-memory database.一些单元测试需要对内存数据库执行一些原始 SQL 查询。
I am trying to switch from sqlite to postgresql inmemory data, using pytest-postgres.我正在尝试使用 pytest-postgres 从 sqlite 切换到 postgresql 内存数据。
s_tb_name = "tb_customer"
ls_cols = ["first_name", "last_name", "email"]
ls_vals = ['("John", "Doe", "john.doe@mail.net")',
'("Jane", "Doe", "jane.doe@mail.net")',
'("Eric", "Dal", "eric.d@home.com")']
s_cols = ', '.join(ls_cols)
s_vals = ', '.join(ls_vals)
session.execute(f"INSERT INTO {s_tb_name} ({s_cols}) VALUES ({s_vals})")
E sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedColumn) column "John" does not exist
E LINE 1: ..., email) VALUES (("John"....
From this psycopg documentation page , I understand this is due to pyscopg2.从这个 psycopg 文档页面,我了解到这是由于 pyscopg2。
It prevents injecting raw dynamic SQL, and it seems I should add this :它可以防止注入原始动态 SQL,看来我应该添加以下内容:
tb_sql_id = sql.Identifier(s_tb_name)
cols_sql_id = sql.SQL(' ,').join(map(sql.Identifier, ls_cols))
vals_sql_id = sql.SQL(' ,').join(map(sql.Literal, ls_vals))
psycopg2_query = sql.SQL(f"INSERT INTO {tb_sql_id} ({cols_sql_id}) VALUES ({vals_sql_id})")
but logically, sqlalchemy refuses to execute the psycopg2_query
:但从逻辑上讲,sqlalchemy 拒绝执行psycopg2_query
:
sqlalchemy.exc.ArgumentError: SQL expression object expected, got object of type <class 'psycopg2.sql.SQL'> instead
Is there a way to execute raw dynamic insert queries in Postgres using SQL Alchemy?有没有办法使用 SQL Alchemy 在 Postgres 中执行原始动态插入查询?
I am compelled to warn you about SQL injection, but since this is for your tests that should not be a concern.我不得不警告您有关 SQL 注入的信息,但由于这是针对您的测试,因此不必担心。
Two changes are needed:需要做两个改动:
ls_vals
need to be enclosed in single, rather than double quotes ls_vals
的值需要用单引号而不是双引号括起来VALUES
need to be removed需要删除VALUES
之后的额外括号 s_tb_name = "tb_customer"
ls_cols = ["first_name", "last_name", "email"]
ls_vals = ["('John', 'Doe', 'john.doe@mail.net')",
"('Jane', 'Doe', 'jane.doe@mail.net')",
"('Eric', 'Dal', 'eric.d@home.com')"]
s_cols = ', '.join(ls_cols)
s_vals = ', '.join(ls_vals)
session.execute(f"INSERT INTO {s_tb_name} ({s_cols}) VALUES {s_vals}")
As pointed by others, injecting SQL like this is to be avoided in most cases.正如其他人所指出的,在大多数情况下应该避免像这样注入 SQL。
Here, the SQL is written in the unit test itself.在这里,SQL 是在单元测试本身中编写的。 There is no external input leaking to the SQL injection, which alleviates the security risk. SQL注入没有外部输入泄露,降低了安全风险。
Mike Organek's solution did not fully work for me, but it pointed me to the right direction : I just had to also remove the parens from ls_vals. Mike Organek 的解决方案并不完全适合我,但它为我指明了正确的方向:我只需要从 ls_vals 中删除括号。
s_tb_name = "tb_customer"
ls_cols = ["first_name", "last_name", "email"]
ls_vals = ["'John', 'Doe', 'john.doe@mail.net'",
"'Jane', 'Doe', 'jane.doe@mail.net'",
"'Eric', 'Dal', 'eric.d@home.com'"]
s_cols = ', '.join(ls_cols)
s_vals = '(' + '), ('.join(ls_vals) + ')'
session.execute(f"INSERT INTO {s_tb_name} ({s_cols}) VALUES {s_vals}")
This made the insert test pass, both when using the sqlite engine and the postgres engine.这使得插入测试通过,无论是在使用 sqlite 引擎还是 postgres 引擎时。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.