Bagaimana cara mengeksekusi SQL mentah di aplikasi Flask-SQLAlchemy

219

Bagaimana Anda menjalankan SQL mentah dalam SQLAlchemy?

Saya memiliki aplikasi web python yang berjalan di labu dan antarmuka ke database melalui SQLAlchemy.

Saya perlu cara untuk menjalankan SQL mentah. Kueri melibatkan beberapa tabel bergabung bersama dengan tampilan Inline.

Saya sudah mencoba:

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

Tapi saya terus mendapatkan kesalahan gateway.

starwing123
sumber
5
Saya telah melihat itu sebelumnya, tetapi saya tidak dapat menemukan tutorial untuk menjalankan pembaruan. Saya juga lebih suka tidak mempelajari sintaks dan rahasia permintaan SQL yang agak panjang (sekitar 20 baris).
starwing123
103
@MarkusUnterwaditzer saya dulu berpikir begitu, tapi sekarang saya sangat tidak setuju. SQL mentah dan parametrized dengan benar umumnya lebih mudah dibaca dan dipelihara daripada sekelompok pemanggilan fungsi dan objek yang menghasilkannya. Ini juga memberi Anda kemampuan penuh dari database tanpa harus melewati rintangan untuk membuat ORM menghasilkan sintaks yang benar (jika itu mungkin) dan menjaga ORM dari melakukan hal-hal yang tidak terduga. Anda mungkin bertanya, "Lalu mengapa menggunakan SQLAlchemy sama sekali?", Dan satu-satunya jawaban yang saya miliki adalah, "Aplikasi yang ada menggunakannya dan mengubah semuanya terlalu mahal."
jpmc26
4
@ jpmc26 Meningkatkan komentar Anda — sebagai pencinta SQL, saya mengalami kesulitan dengan gagasan "memberikan kunci-kunci ke basis data" kepada seorang alkemis yang tidak bertanggung jawab dan cenderung bersandar pada sisi ORM adalah sebuah antipattern :) Yang sedang bergerak kata saya akan tertarik untuk mempercepat komponen tertentu, seperti pendaftaran / manajemen pengguna, dan juga generasi tabel dengan urutan tombol yang saya dapat kode tindakan + SQL. Apakah Anda menemukan beberapa alat yang ramah ORM-skeptic yang bekerja dengan baik untuk Anda dalam kerangka Python?
zx81
@ jpmc26 Apa yang Anda gunakan dalam kerangka Python untuk hanya menggunakan SQL atau cukup dekat seperti C # Dapper? Semua yang saya lihat dalam kerangka kerja web Python ingin saya menggunakan SQLAlchemy, dan saya tidak suka ORM, dan jika saya menggunakannya, ini sangat minimal.
johnny
@ Johnny Saya belum memiliki kesempatan untuk mencobanya sendiri, tetapi perpustakaan koneksi database mentah mungkin cukup. Misalnya, psycopg2 memiliki kursor yang kembali namedtupledan dictsecara langsung: initd.org/psycopg/docs/extras.html .
jpmc26

Jawaban:

310

Sudahkah Anda mencoba:

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

atau:

from sqlalchemy import text

sql = text('select name from penguins')
result = db.engine.execute(sql)
names = [row[0] for row in result]
print names
Miguel
sumber
7
Jika Anda memasukkan atau memperbarui, bagaimana Anda melakukan transaksi?
David S
14
Jika Anda menggunakan SQL mentah maka Anda mengendalikan transaksi, jadi Anda harus menerbitkan BEGINdan COMMITpernyataan sendiri.
Miguel
1
Apakah perintah SQL yang sama berfungsi saat Anda mengeluarkannya tanpa SQLAlchemy? Anda mungkin ingin mengaktifkan debug pada basis data Anda sehingga Anda dapat melihat perintah apa yang dijalankannya.
Miguel
27
db.engine.execute(text("<sql here>")).execution_options(autocommit=True))mengeksekusi dan melakukannya juga.
Devi
8
@Miguel "Jika Anda menggunakan SQL mentah, maka Anda mengendalikan transaksi, jadi Anda harus menerbitkan sendiri pernyataan BEGIN dan COMMIT." Ini tidak benar. Anda bisa menggunakan SQL mentah dengan objek sesi. Baru saja memperhatikan komentar ini, tetapi Anda dapat melihat jawaban saya untuk cara menggunakan sesi dengan SQL mentah.
jpmc26
180

Objek sesi Alkimia SQL memiliki executemetode sendiri :

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

Semua permintaan aplikasi Anda harus melalui objek sesi, apakah itu SQL mentah atau tidak. Ini memastikan bahwa kueri dikelola dengan baik oleh transaksi , yang memungkinkan beberapa kueri dalam permintaan yang sama dilakukan atau dibatalkan sebagai satu kesatuan. Pergi keluar dari transaksi menggunakan mesin atau koneksi membuat Anda berisiko lebih besar untuk tersamar, mungkin sulit untuk mendeteksi bug yang dapat membuat Anda dengan data yang rusak. Setiap permintaan harus dikaitkan dengan hanya satu transaksi, dan menggunakan db.sessionakan memastikan ini adalah kasus untuk aplikasi Anda.

Perhatikan juga yang executedirancang untuk kueri parameter . Gunakan parameter, seperti :valpada contoh, untuk setiap input ke kueri untuk melindungi diri Anda dari serangan injeksi SQL. Anda dapat memberikan nilai untuk parameter ini dengan melewatkan adict argumen kedua, di mana setiap kunci adalah nama parameter seperti yang muncul dalam kueri. Sintaks yang tepat dari parameter itu sendiri mungkin berbeda tergantung pada database Anda, tetapi semua database relasional utama mendukungnya dalam beberapa bentuk.

Dengan asumsi itu sebuah SELECTquery, ini akan mengembalikan sebuah iterable dariRowProxy objek.

Anda dapat mengakses setiap kolom dengan berbagai teknik:

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

Secara pribadi, saya lebih suka mengonversi hasilnya menjadi namedtuples:

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)

Jika Anda tidak menggunakan ekstensi Flask-SQLAlchemy, Anda masih dapat dengan mudah menggunakan sesi:

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})
jpmc26
sumber
Select akan mengembalikan ResultProxy.
Alan B
@ Pesawat Ya. Saya memilih kata-kata saya dengan buruk ketika saya menyebutnya urutan, menyiratkannya mengimplementasikan protokol urutan. Saya sudah mengoreksi dan mengklarifikasi. Terima kasih.
jpmc26
@ jpmc26 harus menutup sesi setelah menjalankan kueri seperti db.session.close ()? Dan apakah masih memiliki manfaat koneksi pooling?
ravi malhotra
58

docs: Tutorial Bahasa Ekspresi SQL - Menggunakan Teks

contoh:

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')
Jake Berger
sumber
1
Tautan ke dokumen sqlalchemy tampaknya kedaluwarsa. Ini lebih baru: docs.sqlalchemy.org/en/latest/core/…
Carl
1
Bolehkah saya bertanya mengapa kami menggunakan ==?
Nam G VU
1
@ Jake Berger, terima kasih banyak untuk Anda. Saya sudah menghabiskan hampir sehari untuk mencari jawaban ini. Saya hanya langsung menjalankan sql tanpa mengkonversi ke teks. Itu melempar kesalahan setiap kali kita memiliki% siswa% di klausa tempat saya. Tepuk tangan meriah atas jawaban Anda.
Suresh Kumar
1
@NamGVU karena seperti dalam kebanyakan bahasa pemrograman, =biasanya disediakan untuk menetapkan nilai; sedangkan ==dicadangkan untuk membandingkan nilai
Jake Berger
2
@JakeBerger Anda punya tautan untuk itu? SQL bukan bahasa seperti itu, dan menilai oleh dokumen SQLAlchemy ini tidak begitu.
johndodo
36

Anda bisa mendapatkan hasil dari query SQL SELECT menggunakan from_statement()dan text()seperti yang ditunjukkan di sini . Anda tidak harus berurusan dengan tuple dengan cara ini. Sebagai contoh untuk kelas yang Usermemiliki nama tabel usersyang dapat Anda coba,

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

return user
TrigonaMinima
sumber
15
result = db.engine.execute(text("<sql here>"))

mengeksekusi <sql here>tetapi tidak melakukan itu kecuali Anda berada diautocommit mode. Jadi, sisipan dan pembaruan tidak akan tercermin dalam database.

Untuk melakukan setelah perubahan, lakukan

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

Ini adalah jawaban yang disederhanakan tentang cara menjalankan query SQL dari Flask Shell

Pertama, petakan modul Anda (jika modul / aplikasi Anda manage.py di folder utama dan Anda berada di sistem Operasi UNIX), jalankan:

export FLASK_APP=manage

Jalankan Flask shell

flask shell

Impor apa yang kita butuhkan ::

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

Jalankan kueri Anda:

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

Ini menggunakan koneksi database saat ini yang memiliki aplikasi.

Luigi Lopez
sumber
0

Sudahkah Anda mencoba menggunakan connection.execute(text( <sql here> ), <bind params here> )dan mengikat parameter seperti yang dijelaskan dalam dokumen ? Ini dapat membantu memecahkan banyak pemformatan parameter dan masalah kinerja. Mungkin kesalahan gateway adalah batas waktu? Parameter binding cenderung membuat query kompleks dieksekusi lebih cepat.

jhnwsk
sumber
2
menurut dokumen , seharusnya connection.execute(text(<sql here>), <bind params> ). bind paramsTIDAK harus dalam text(). memberi makan dalam parameter bind ke metode execute ()
Jake Berger
Tautan Jake rusak. Saya pikir ini adalah URL yang relevan sekarang: docs.sqlalchemy.org/en/latest/core/…
code_dredd
-1

Jika Anda ingin menghindari tupel, cara lain adalah dengan memanggil first, oneatau allmetode:

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

assert query.first().name == "Welcome to my blog"
Joe Gasewicz
sumber