如何在Flask-SQLAlchemy应用中执行原始SQL

309 人关注

如何在SQLAlchemy中执行原始SQL?

我有一个运行在flask上的python网络应用,通过SQLAlchemy与数据库对接。

我需要一种方法来运行原始SQL。该查询涉及多个表的连接和内联视图。

I've tried:

connection = db.session.connection()
connection.execute( <sql here> )

但我一直收到网关错误。

5 个评论
我以前也看过,但我找不到运行更新的教程。我也不愿意学习语法和掩盖一个相当长的(大约20行)SQL查询。
@MarkusUnterwaditzer 我曾经这样认为,但现在我坚决不同意。原始的、正确的参数化的SQL通常比一堆函数调用和生成对象更容易阅读和维护。它还能让你享受到数据库的全部功能,而不必为了让ORM生成正确的语法而大费周章(如果有可能的话),并让ORM不做意想不到的事情。你可能会问,"那为什么还要使用SQLAlchemy呢?",我的唯一答案是,"现有的应用程序使用它,改变一切太昂贵了"。
zx81
@jpmc26 提高了你的评论--作为一个SQL的爱好者,我很难接受将 "数据库的钥匙交给一个不负责任的炼金术士 "的想法,并倾向于倾向于以下的观点 ORM是一种反模式 :)也就是说,我很想加速某些组件,比如用户注册/管理,以及生成带有按钮序列的表格,我可以用SQL来编写动作。你是否遇到过一些对ORM持怀疑态度的工具,它们在Python框架中对你很有效?
@jpmc26 在Python框架中,你用什么来只用SQL或者像C# Dapper那样相当接近? 我在Python网络框架中看到的所有东西都想让我使用SQLAlchemy,我不喜欢ORM,如果我使用了ORM,也是极其简单的。
接受的答案是废弃的。请看下面@Demitri的回答。
python
sql
sqlalchemy
flask
flask-sqlalchemy
starwing123
starwing123
发布于 2013-07-31
9 个回答
Miguel Grinberg
Miguel Grinberg
发布于 2019-12-25
已采纳
0 人赞同

你有没有试过。

result = db.engine.execute("<sql here>")
from sqlalchemy import text
sql = text('select name from penguins')
result = db.engine.execute(sql)
names = [row[0] for row in result]
print names

请注意,db.engine.execute()是 "无连接",也就是在SQLAlchemy 2.0中被弃用。.

如果你做了一个插入或更新,你如何提交交易?
如果你使用的是原始SQL,那么你就控制了事务,所以你必须自己发布 BEGIN COMMIT 语句。
Devi
db.engine.execute(text("<sql here>")).execution_options(autocommit=True)) 执行并提交它。
@Miguel "如果你使用的是原始SQL,那么你就控制了事务,所以你必须自己发布BEGIN和COMMIT语句。"这根本不是真的。你可以用会话对象使用原始SQL。刚注意到这个评论,但你可以看看我对如何使用原始SQL的会话的回答。
我想知道为什么我们需要用 text(...) 来包装查询?
jpmc26
jpmc26
发布于 2019-12-25
0 人赞同

SQL Alchemy会话对象有自己的 execute 方法。

result = db.session.execute('SELECT * FROM my_table WHERE my_column = :val', {'val': 5})

全部你的应用程序查询应该通过一个会话对象,无论它们是否是原始SQL。这可以确保查询是正确的由交易管理的它允许同一请求中的多个查询作为一个单元被提交或回滚。在事务之外使用engine or the connection让你面临更大的风险,可能难以发现的错误会让你的数据被破坏。每个请求应该只与一个事务相关联,使用db.session将确保你的应用程序是这样的。

还需要注意的是,execute是针对参数化查询.使用参数,如例子中的:val,用于查询的任何输入,以保护自己免受SQL注入攻击。你可以通过传递dict作为第二个参数来提供这些参数的值,其中每个键是参数的名称,因为它出现在查询中。参数本身的确切语法可能因你的数据库而异,但所有主要的关系型数据库都以某种形式支持它们。

假设它是一个SELECT的查询,这将返回一个可迭代的对象 of RowProxy objects.

你可以用各种技术访问各个栏目。

for r in result:
    print(r[0]) # Access by positional index
    print(r['my_column']) # Access by column name as a string
    r_dict = dict(r.items()) # convert to dict keyed by column names

就个人而言,我更喜欢将结果转换成namedtuple

from collections import namedtuple
Record = namedtuple('Record', result.keys())
records = [Record(*r) for r in result.fetchall()]
for r in records:
    print(r.my_column)
    print(r)
from sqlalchemy.orm import sessionmaker, scoped_session
engine = sqlalchemy.create_engine('my connection string')
Session = scoped_session(sessionmaker(bind=engine))
s = Session()
result = s.execute('SELECT * FROM my_table WHERE my_column = :val', {'val': 5})
    
一个选择将返回一个ResultProxy。
@AlanB 是的。当我称它为一个序列时,我选择的措辞很差,暗示它实现了序列协议。我已经改正并澄清了。谢谢。
@jpmc26 在执行完查询后是否应该像db.session.close()那样关闭会话?它还会有连接池的好处吗?
dict(r.items()) its works. make sure you have proper version of these two packages. SQLAlchemy=<1.3.5, Flask-SQLAlchemy=<2.4.1
With these I was always getting lists and TypeError: list indices errors ... So finally I went with results = db_session.query("my_table")........ .all() and then for row in results: and then row.my_column worked, or if you have a the column name in a variable => then I recommend getattr(row, column_name_str_variable)
Jake Berger
Jake Berger
发布于 2019-12-25
0 人赞同

docs: SQL表达式语言教程 - 使用文本

from sqlalchemy.sql import text
connection = engine.connect()
# recommended
cmd = 'select * from Employees where EmployeeGroup = :group'
employeeGroup = 'Staff'
employees = connection.execute(text(cmd), group = employeeGroup)
# or - wee more difficult to interpret the command
employeeGroup = 'Staff'
employees = connection.execute(
                  text('select * from Employees where EmployeeGroup = :group'), 
                  group = employeeGroup)
# or - notice the requirement to quote 'Staff'
employees = connection.execute(
                  text("select * from Employees where EmployeeGroup = 'Staff'"))
for employee in employees: logger.debug(employee)
# output
(0, 'Tim', 'Gurra', 'Staff', '991-509-9284')
(1, 'Jim', 'Carey', 'Staff', '832-252-1910')
(2, 'Lee', 'Asher', 'Staff', '897-747-1564')
(3, 'Ben', 'Hayes', 'Staff', '584-255-2631')
    
Carl
到sqlalchemy文档的链接似乎已经过时了。这个是比较新的。 docs.sqlalchemy.org/en/latest/core/...
请问我们为什么要用 ==
@Jake Berger非常感谢你。我为了寻找这个答案几乎浪费了一天的时间。我只是直接执行sql而没有转换为文本。只要我的where子句中有%students%,它就会出现错误。请为你的回答鼓掌。
@NamGVU 因为在大多数编程语言中, = 通常被保留给 指派 一个值;而 == 则是保留给 比较 价值
@JakeBerger 你有这方面的链接吗?SQL不是这样的语言,而且从 SQLAlchemy docs this is not so.
TrigonaMinima
TrigonaMinima
发布于 2019-12-25
0 人赞同

你可以使用 from_statement() text() 获得SELECT SQL查询的结果,如图所示 here .你不必以这种方式来处理图元。作为一个例子,对于一个具有表名 User 的类,你可以尝试。

from sqlalchemy.sql import text
user = session.query(User).from_statement(
    text("""SELECT * FROM users where name=:name""")
).params(name="ed").all()
return user
    
Demitri
Demitri
发布于 2019-12-25
0 人赞同

For SQLAlchemy ≥ 1.4

从SQLAlchemy 1.4开始,无连接或隐式执行已被弃用,即

db.engine.execute(...) # DEPRECATED

以及作为查询的裸字符串。

新的API需要一个明确的连接,比如说

from sqlalchemy import text
with db.engine.connect() as connection:
    result = connection.execute(text("SELECT * FROM ..."))
    for row in result:
        # ...

同样地,我们鼓励使用一个现有的Session如果有的话。

result = session.execute(sqlalchemy.text("SELECT * FROM ..."))

或使用参数。

session.execute(sqlalchemy.text("SELECT * FROM a_table WHERE a_column = :val"),
                {'val': 5})

See "Connectionless Execution, Implicit Execution",以了解更多细节。

现在,这应该是公认的答案。
0 人赞同
result = db.engine.execute(text("<sql here>"))

执行<sql here>,但不提交,除非你是在autocommit模式。因此,插入和更新不会反映在数据库中。

要在修改后提交,请执行

result = db.engine.execute(text("<sql here>").execution_options(autocommit=True))
    
Luigi Lopez
Luigi Lopez
发布于 2019-12-25
0 人赞同

这是一个关于如何从Flask Shell运行SQL查询的简化答案

首先,映射你的模块(如果你的模块/应用在principal文件夹中是manage.py,并且你在UNIX操作系统中),运行。

export FLASK_APP=manage

运行Flask外壳

flask shell

进口我们需要的东西:。

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy(app)
from sqlalchemy import text

Run your query:

result = db.engine.execute(text("<sql here>").execution_options(autocommit=True))

这将使用当前拥有应用程序的数据库连接。

jhnwsk
jhnwsk
发布于 2019-12-25
0 人赞同

你是否尝试过使用 connection.execute(text( <sql here> ), <bind params here> ) ,并按照描述的方式绑定参数? in the docs ?这可以帮助解决许多参数的格式和性能问题。也许网关的错误是一个超时?绑定参数往往能使复杂的查询执行速度大大加快。

根据 docs , it should be connection.execute(text(<sql here>), <bind params> ) . bind params should NOT be in text() . 向execute()方法输入绑定的参数
杰克的链接已经坏了。我想这是现在相关的URL。 docs.sqlalchemy.org/en/latest/core/...
Joe Gasewicz
Joe Gasewicz
发布于 2019-12-25
0 人赞同

如果你想避免使用图元,另一种方法是调用 first one all 方法。