Flask-SQLAlchemy uygulamasında ham SQL nasıl çalıştırılır


219

SQLAlchemy'de ham SQL'i nasıl yürütürsünüz?

SQLAlchemy aracılığıyla veritabanına şişesi ve arayüzleri üzerinde çalışan bir python web uygulaması var.

Ham SQL çalıştırmak için bir yol gerekir. Sorgu, Satır içi görünümlerle birlikte birden çok tablo birleştirmesini içerir.

Denedim:

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

Ama ağ geçidi hataları alıyorum.


5
Buna daha önce baktım, ancak güncelleme güncelleme hakkında bir eğitim bulamadım. Ayrıca sözdizimini öğrenmek ve oldukça uzun (yaklaşık 20 satır) bir SQL sorgusu örtmek istiyorum.
starwing123

103
@MarkusUnterwaditzer Bunu düşünürdüm, ama şimdi kesinlikle katılmıyorum. Ham, düzgün bir şekilde parametrelenmiş SQL'i okumak ve korumak genellikle bir grup işlev çağrısı ve onu üreten nesnelerden çok daha kolaydır. Ayrıca ORM'in doğru sözdizimini (hatta mümkünse) oluşturmasını sağlamak için çemberlerden atlamak zorunda kalmadan veritabanının tam yeteneklerini sağlar ve ORM'in beklenmedik şeyler yapmasını engeller. "Öyleyse neden SQLAlchemy kullanıyorsunuz?" Sorusunu sorabilirsiniz ve sahip olduğum tek yanıt, "Mevcut uygulama bunu kullanıyor ve her şeyi değiştirmek çok pahalı."
jpmc26

4
@ jpmc26 Yorumunuzu aştı - SQL'in bir sevgilisi olarak, sorumsuz bir simyacıya "veritabanının anahtarlarını vermek" fikri ile zor zamanlarım var ve ORM'nin yanına yaslanma eğilimi bir antipattern :) Bu varlık kullanıcı kaydı / yönetimi gibi bazı bileşenleri ve ayrıca eylemleri + SQL kodlayabileceğiniz düğme dizileri içeren tabloların oluşturulmasını hızlandırmak istediğimi söyledi. Python çerçevesinde sizin için iyi çalışan bazı ORM şüpheci dostu araçlarla karşılaştınız mı?
zx81

@ jpmc26 Python çerçevesinde sadece SQL kullanmak veya C # Dapper gibi oldukça yakın olmak için ne kullanıyorsunuz? Bir Python web çerçevesinde gördüğüm her şey SQLAlchemy kullanmamı istiyor ve bir ORM'den hoşlanmıyorum ve eğer bir tane kullanırsam son derece azdır.
johnny

@johnny Kendim denemek için bir fırsatım olmadı, ancak ham veritabanı bağlantı kitaplıkları muhtemelen yeterli. Örneğin, psycopg2 geri dönen imleçlere sahiptir namedtuplevedict : Doğrudan initd.org/psycopg/docs/extras.html .
jpmc26

Yanıtlar:


310

Denedin mi:

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

veya:

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
Ekleme veya güncelleme yaparsanız işlemi nasıl gerçekleştirirsiniz?
David S

14
Ham SQL kullanıyorsanız, işlemleri kontrol edersiniz, bu nedenle BEGINve COMMITifadelerini kendiniz yayınlamanız gerekir .
Miguel

1
SQLAlchemy olmadan bunları verdiğinizde aynı SQL komutları çalışır mı? Hangi komutları yürüttüğünü görebilmeniz için veritabanınızda hata ayıklamayı etkinleştirmek isteyebilirsiniz.
Miguel

27
db.engine.execute(text("<sql here>")).execution_options(autocommit=True))yürütür ve taahhüt eder.
Devi

8
@Miguel "Ham SQL kullanıyorsanız işlemleri kontrol edersiniz, bu nedenle BEGIN ve COMMIT deyimlerini kendiniz yayınlamanız gerekir." Bu doğru değil. Ham SQL'i bir oturum nesnesiyle kullanabilirsiniz. Sadece bu yorumu fark ettim, ancak ham SQL ile bir oturumun nasıl kullanılacağına dair cevabımı görebilirsiniz.
jpmc26

180

SQL Alchemy oturum nesnelerinin kendi executeyöntemleri vardır:

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

Tüm uygulama sorgularınız ham SQL olsun veya olmasın bir oturum nesnesinden geçmelidir. Bu, sorguların aynı istekte birden çok sorgunun tek bir birim olarak işlenmesini veya geri alınmasını sağlayan bir işlem tarafından düzgün bir şekilde yönetilmesini sağlar. Motoru veya bağlantıyı kullanarak işlemin dışına çıkmak , sizi daha büyük ince risklere sokar, muhtemelen bozuk verilerle karşılaşmanıza neden olabilecek hataları tespit etmek zordur. Her istek yalnızca bir işlemle ilişkilendirilmelidir ve bunu kullanmak db.sessionuygulamanız için durumun geçerli olmasını sağlayacaktır.

Ayrıca parametreli sorgularexecute için tasarlanmış notları da dikkate alın . Kendinizi SQL enjeksiyon saldırılarına karşı korumak için sorguya yapılan tüm girdiler için örnekte olduğu gibi parametreleri kullanın . A İkinci argüman olarak ileterek bu parametreler için değer sağlayabilirsiniz; burada her anahtar, sorguda göründüğü şekliyle parametrenin adıdır. Parametrenin kendisinin tam sözdizimi, veritabanınıza bağlı olarak farklı olabilir, ancak tüm önemli ilişkisel veritabanları bunları bir biçimde destekler.:valdict

Bunun bir SELECTsorgu olduğunu varsayarsak , bu yinelenebilir bir RowProxynesne döndürür .

Sütunlara çeşitli tekniklerle erişebilirsiniz:

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

Şahsen, sonuçları namedtuples'ye dönüştürmeyi tercih ederim :

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)

Flask-SQLAlchemy uzantısını kullanmıyorsanız, yine de kolayca bir oturum kullanabilirsiniz:

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

Seç, bir ResultProxy döndürür.
Alan B

@AlanB Evet. Bir dizi dediğimde kelimelerimi kötü seçtim, bu dizi protokolünü uyguladığını ima ediyordum. Düzelttim ve netleştirdim. Teşekkürler.
jpmc26

@ jpmc26 db.session.close () gibi sorguyu yürüttükten sonra oturumu kapatmalı mı? Ve yine de bağlantı havuzunun faydaları olacak mı?
ravi malhotra

58

docs: SQL İfade Dili Eğitimi - Metin Kullanma

misal:

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
Sqlalchemy belgelerine bağlantı güncel değil gibi görünüyor. Bu daha yeni: docs.sqlalchemy.org/tr/latest/core/…
Carl

1
Neden kullandığımızı sorabilir miyim ==?
Nam G VU

1
@ Berger senin için çok teşekkürler. Bu cevabı bulmak için neredeyse bir gün harcadım. Metne dönüştürmeden doğrudan sql yürütüyordum. Nerede bulunan cümlemde% öğrenci% olduğunda hata atıyordu. Cevabınız için büyük bir alkış.
Suresh Kumar

1
@NamGVU, çoğu programlama dilinde olduğu gibi =, normalde bir değer atamak için ayrılmıştır ; oysa ==ayrılmıştır karşılaştırırken değerleri
Jake Berger

2
@JakeBerger Bunun için bir bağlantınız var mı? SQL böyle bir dil değildir ve SQLAlchemy belgelerine göre bu böyle değildir.
johndodo

36

Burada gösterildiği gibi from_statement()ve kullanarak SELECT SQL sorgularının sonuçlarını alabilirsiniz . Tuples ile bu şekilde uğraşmak zorunda değilsiniz. Tablo adına sahip bir sınıfa örnek olarak deneyebilirsiniz,text()Userusers

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

yürütür, <sql here>ancak yapmadığınız sürece taahhüt etmezautocommit modda . Bu nedenle, eklemeler ve güncellemeler veritabanına yansıtılmaz.

Değişikliklerden sonra taahhütte bulunmak için

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

2

Bu, Flask Shell'den SQL sorgusunun nasıl çalıştırılacağına dair basitleştirilmiş bir cevaptır

İlk olarak modülünüzü eşleyin (modül / uygulamanız asıl klasörde manage.py ise ve bir UNIX İşletim sistemindeyseniz) çalıştırın:

export FLASK_APP=manage

Flask kabuğunu çalıştır

flask shell

İhtiyacımız olan şeyleri içe aktarın ::

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

Sorgunuzu çalıştırın:

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

Bu, uygulamanın bulunduğu veritabanı bağlantısını kullanır.


0

Dokümanlardaconnection.execute(text( <sql here> ), <bind params here> ) açıklandığı gibi parametreleri kullanmayı ve bağlamayı denediniz mi? Bu, birçok parametre biçimlendirme ve performans sorununu çözmenize yardımcı olabilir. Belki ağ geçidi hatası zaman aşımıdır? Bağlama parametreleri, karmaşık sorguların önemli ölçüde daha hızlı yürütülmesini sağlama eğilimindedir.


2
dokümanlara göre öyle olmalı connection.execute(text(<sql here>), <bind params> ). bind paramsiçinde olmamalı text(). bind parametrelerini execute () yöntemine besleme
Jake Berger

Jake'in bağlantısı koptu. Sanırım şu anda alakalı olan URL bu: docs.sqlalchemy.org/en/latest/core/…
code_dredd

-1

Tuplelerden kaçınmak istiyorsanız, başka bir yol first, oneveya allyöntemlerini çağırmaktır :

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

assert query.first().name == "Welcome to my blog"
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.