Cách thực thi SQL thô trong ứng dụng Flask-SQLAlchemy


218

Làm thế nào để bạn thực thi SQL thô trong SQLAlchemy?

Tôi có một ứng dụng web python chạy trên bình và giao diện với cơ sở dữ liệu thông qua SQLAlchemy.

Tôi cần một cách để chạy SQL thô. Truy vấn liên quan đến nhiều bảng tham gia cùng với các khung nhìn Inline.

Tôi đã thử:

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

Nhưng tôi cứ bị lỗi cổng.


5
Tôi đã xem xét điều đó trước đây, nhưng tôi không thể tìm thấy một hướng dẫn về việc chạy một bản cập nhật. Tôi cũng không muốn học cú pháp và chuyển đổi một truy vấn SQL khá dài (khoảng 20 dòng).
starwing123

103
@MarkusUnterwaditzer Tôi đã từng nghĩ như vậy, nhưng bây giờ tôi hoàn toàn không đồng ý. SQL thô, được tham số hóa chính xác thường dễ đọc và bảo trì hơn nhiều so với một loạt các lệnh gọi và đối tượng tạo ra nó. Nó cũng cung cấp cho bạn toàn bộ khả năng của cơ sở dữ liệu mà không cần phải nhảy qua các vòng để làm cho ORM tạo cú pháp chính xác (nếu có thể) và giữ cho ORM không làm những điều không mong muốn. Bạn có thể đặt câu hỏi: "Vậy thì tại sao lại sử dụng SQLAlchemy?" Và câu trả lời duy nhất tôi có là "Ứng dụng hiện có sử dụng nó và thay đổi mọi thứ quá đắt."
jpmc26

4
@ jpmc26 Đã đưa ra nhận xét của bạn. Là một người yêu thích SQL, tôi có một thời gian khó khăn với ý tưởng "trao chìa khóa cho cơ sở dữ liệu" cho một nhà giả kim vô trách nhiệm và có xu hướng nghiêng về phía ORM là một phản đề :) cho biết tôi rất muốn tăng tốc một số thành phần nhất định, chẳng hạn như đăng ký / quản lý người dùng và cả việc tạo các bảng với chuỗi các nút mà tôi có thể mã hóa các hành động + SQL. Bạn đã bắt gặp một số công cụ thân thiện với ORM hoài nghi hoạt động tốt cho bạn trong khung Python chưa?
zx81

@ jpmc26 Bạn sử dụng gì trong khung Python để chỉ sử dụng SQL hoặc khá gần như C # Dapper? Mọi thứ tôi thấy trong khung web Python đều muốn tôi sử dụng SQLAlchemy và tôi không thích ORM và nếu tôi sử dụng một cái, nó cực kỳ tối thiểu.
johnny

@johnny Tôi chưa có cơ hội dùng thử, nhưng các thư viện kết nối cơ sở dữ liệu thô có lẽ là đủ. Ví dụ: psycopg2 có các con trỏ trả về namedtupledicttrực tiếp: initd.org/psycopg/docs/extras.html .
jpmc26

Câu trả lời:


309

Bạn đã thử chưa:

result = db.engine.execute("<sql here>")

hoặc là:

from sqlalchemy import text

sql = text('select name from penguins')
result = db.engine.execute(sql)
names = [row[0] for row in result]
print names

7
Nếu bạn thực hiện chèn hoặc cập nhật, làm thế nào để bạn thực hiện giao dịch?
David S

14
Nếu bạn đang sử dụng SQL thô thì bạn sẽ kiểm soát các giao dịch, do đó bạn phải tự phát hành BEGINCOMMITtuyên bố.
Miguel

1
Các lệnh SQL tương tự có hoạt động khi bạn phát hành chúng mà không có SQLAlchemy không? Bạn có thể muốn bật gỡ lỗi trên cơ sở dữ liệu của mình để bạn có thể thấy những lệnh mà nó đang thực thi.
Miguel

27
db.engine.execute(text("<sql here>")).execution_options(autocommit=True))thực hiện và cam kết nó quá.
Devi

8
@Miguel "Nếu bạn đang sử dụng SQL thô thì bạn sẽ kiểm soát các giao dịch, do đó bạn phải tự phát hành BEGIN và CAM KẾT." Đơn giản là nó sai. Bạn có thể sử dụng SQL thô với một đối tượng phiên. Chỉ cần lưu ý nhận xét này, nhưng bạn có thể thấy câu trả lời của tôi về cách sử dụng phiên với SQL thô.
jpmc26

180

Các đối tượng phiên Alchemy SQL có executephương thức riêng của chúng :

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

Tất cả các truy vấn ứng dụng của bạn phải trải qua một đối tượng phiên, cho dù chúng là SQL thô hay không. Điều này đảm bảo rằng các truy vấn được quản lý đúng bởi một giao dịch , cho phép nhiều truy vấn trong cùng một yêu cầu được cam kết hoặc khôi phục dưới dạng một đơn vị. Đi ra ngoài giao dịch bằng cách sử dụng công cụ hoặc kết nối sẽ khiến bạn có nguy cơ tinh vi cao hơn, khó có thể phát hiện ra các lỗi có thể khiến bạn bị hỏng dữ liệu. Mỗi yêu cầu chỉ được liên kết với một giao dịch và việc sử dụng db.sessionsẽ đảm bảo đây là trường hợp cho ứng dụng của bạn.

Cũng lưu ý rằng executeđược thiết kế cho các truy vấn tham số . Sử dụng các tham số, như :valtrong ví dụ, cho bất kỳ đầu vào nào cho truy vấn để bảo vệ bạn khỏi các cuộc tấn công tiêm nhiễm SQL. Bạn có thể cung cấp giá trị cho các tham số này bằng cách chuyển một dictđối số thứ hai, trong đó mỗi khóa là tên của tham số như xuất hiện trong truy vấn. Cú pháp chính xác của chính tham số có thể khác nhau tùy thuộc vào cơ sở dữ liệu của bạn, nhưng tất cả các cơ sở dữ liệu quan hệ chính đều hỗ trợ chúng ở một số dạng.

Giả sử nó là một SELECT truy vấn, điều này sẽ trả về một lần lặp của RowProxycác đối tượng.

Bạn có thể truy cập các cột riêng lẻ với nhiều kỹ thuật khác nhau:

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

Cá nhân, tôi thích chuyển đổi kết quả thành namedtuple s:

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)

Nếu bạn không sử dụng tiện ích mở rộng Flask-SQLAlchemy, bạn vẫn có thể dễ dàng sử dụng phiên:

import sqlalchemy
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})

Chọn sẽ trả về một Kết quả.
Alan B

@AlanB Vâng. Tôi đã chọn từ ngữ kém khi tôi gọi nó là một chuỗi, ngụ ý nó thực hiện giao thức chuỗi. Tôi đã sửa chữa và làm rõ. Cảm ơn.
jpmc26

@ jpmc26 có nên đóng phiên sau khi thực hiện truy vấn như db.session.close () không? Và nó vẫn sẽ có những lợi ích của việc kết nối?
ravi malhotra

58

tài liệu: Hướng dẫn ngôn ngữ biểu thức SQL - Sử dụng văn bản

thí dụ:

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')

1
Liên kết đến các tài liệu sqlalchemy dường như đã lỗi thời. Đây là gần đây hơn: docs.sqlalchemy.org/en/latest/core/ Kẻ
Carl

1
Tôi có thể hỏi tại sao chúng ta sử dụng ==?
Nam G VU

1
@Jake Berger một lời cảm ơn lớn cho bạn. Tôi đã lãng phí gần một ngày để tìm kiếm câu trả lời này. Tôi chỉ trực tiếp thực hiện sql mà không chuyển đổi thành văn bản. Đó là lỗi ném bất cứ khi nào chúng tôi có% student% trong mệnh đề where của tôi. Một tràng pháo tay lớn cho câu trả lời của bạn.
Suresh Kumar

1
@NamGVU vì giống như trong hầu hết các ngôn ngữ lập trình, =thường được dành riêng để gán giá trị; trong khi đó ==được dành riêng để so sánh các giá trị
Jake Berger

2
@JakeBerger Bạn có một liên kết cho điều đó? SQL không phải là một ngôn ngữ như vậy và việc đánh giá bởi các tài liệu SQLAlchemy thì điều này không phải vậy.
johndodo

36

Bạn có thể nhận được kết quả của các truy vấn SQL CHỌN bằng cách sử dụng from_statement()text()như được hiển thị ở đây . Bạn không phải đối phó với các bộ dữ liệu theo cách này. Để làm ví dụ cho một lớp Usercó tên bảng usersbạn có thể thử,

from sqlalchemy.sql import text
.
.
.
user = session.query(User).from_statement(
    text("SELECT * FROM users where name=:name")).\
    params(name='ed').all()

return user

15
result = db.engine.execute(text("<sql here>"))

thực thi <sql here>nhưng không cam kết trừ khi bạn vàoautocommit chế độ. Vì vậy, chèn và cập nhật sẽ không phản ánh trong cơ sở dữ liệu.

Để cam kết sau khi thay đổi, hãy làm

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

2

Đây là câu trả lời đơn giản về cách chạy truy vấn SQL từ Flask Shell

Trước tiên, hãy ánh xạ mô-đun của bạn (nếu mô-đun / ứng dụng của bạn là Manage.txt trong thư mục chính và bạn đang ở trong Hệ điều hành UNIX), hãy chạy:

export FLASK_APP=manage

Chạy vỏ bình

flask shell

Nhập những gì chúng ta cần ::

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

Chạy truy vấn của bạn:

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

Điều này sử dụng kết nối cơ sở dữ liệu hiện có ứng dụng.


0

Bạn đã thử sử dụng connection.execute(text( <sql here> ), <bind params here> )và liên kết các tham số như được mô tả trong các tài liệu chưa? Điều này có thể giúp giải quyết nhiều vấn đề định dạng và hiệu suất tham số. Có lẽ lỗi cổng là một thời gian chờ? Các tham số ràng buộc có xu hướng làm cho các truy vấn phức tạp thực hiện nhanh hơn đáng kể.


2
theo tài liệu , nó nên được connection.execute(text(<sql here>), <bind params> ). bind paramsKHÔNG nên ở trong text(). cung cấp các tham số liên kết cho phương thức exec ()
Jake Berger

Liên kết của Jake bị hỏng. Tôi nghĩ rằng đây là URL có liên quan ngay bây giờ: docs.sqlalchemy.org/en/latest/core/ mẹo
code_dredd

-1

Nếu bạn muốn tránh các bộ, một cách khác là bằng cách gọi first, onehoặc allphương pháp:

query = db.engine.execute("SELECT * FROM blogs "
                           "WHERE id = 1 ")

assert query.first().name == "Welcome to my blog"
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.