Як виконати необроблений SQL в додатку Flask-SQLAlchemy


219

Як виконати необроблений SQL в SQLAlchemy?

У мене є веб-додаток python, яке працює на колбі та інтерфейси до бази даних через SQLAlchemy.

Мені потрібен спосіб запустити необроблений SQL. Запит включає декілька приєднань таблиці разом із вбудованими поданнями.

Я спробував:

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

Але я продовжую отримувати помилки на шлюзі.


5
Я раніше це переглядав, але не зміг знайти підручник із запуску оновлення. Я також не хотів би вивчити синтаксис і приховати досить довгий (близько 20 рядків) SQL-запит.
starwing123

103
@MarkusUnterwaditzer Я раніше так думав, але зараз я категорично не згоден. Сирий, правильно параметризований SQL, як правило, набагато простіше читати та підтримувати, ніж купу функціональних викликів та об’єктів, які його генерують. Це також надає вам всі можливості бази даних без необхідності стрибати через обручі, щоб ORM генерував правильний синтаксис (якщо це навіть можливо) і не дозволяє ORM робити несподівані речі. Ви можете задати питання "Тоді навіщо взагалі використовувати SQLAlchemy?", І єдина відповідь, яку я маю, - "Існуюча програма використовує її, і змінити все занадто дорого".
jpmc26

4
@ jpmc26 Опублікував ваш коментар - як любитель SQL, мені важко з ідеєю "віддати ключі до бази даних" безвідповідальному алхіміку і, як правило, схилятися на бік ORM - це антипатерн :) Це істота сказав, що я б хотів прискорити певні компоненти, такі як реєстрація / управління користувачами, а також генерування таблиць із послідовностями кнопок, для яких я можу кодувати дії + SQL. Чи натрапили ви на деякі сприятливі до ORM-скептики інструменти, які добре працюють для вас в рамках Python?
zx81

@ jpmc26 Що ви використовуєте в рамках Python, щоб використовувати просто SQL або досить близько, як C # Dapper? Все, що я бачу у веб-рамках Python, хоче, щоб я використовував SQLAlchemy, і мені не подобається ORM, і якщо я використовую, це надзвичайно мінімально.
Johnny

@johnny У мене не було можливості спробувати це самостійно, але сировинних бібліотек підключення до бази даних, мабуть, достатньо. Наприклад, у psycopg2 є курсори, які повертаються namedtupleта dictбезпосередньо: initd.org/psycopg/docs/extras.html .
jpmc26

Відповіді:


310

Ти намагався:

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

7
Якщо ви робите вставку чи оновлення, як здійснювати транзакцію?
David S

14
Якщо ви використовуєте необроблений SQL, то ви керуєте транзакціями, тому вам доведеться видавати дані BEGINі COMMITзаяви самостійно.
Мігель

1
Чи працюють ті самі команди SQL, коли ви видаєте їх без SQLAlchemy? Ви можете увімкнути налагодження у вашій базі даних, щоб ви могли бачити, які команди вона виконує.
Мігель

27
db.engine.execute(text("<sql here>")).execution_options(autocommit=True))виконує і вчиняє його теж.
Деві

8
@Miguel "Якщо ви використовуєте необроблений SQL, ви керуєте транзакціями, тому вам доведеться самостійно видавати оператори BEGIN і COMMIT." Це просто неправда. Ви можете використовувати необроблений SQL з об’єктом сеансу. Щойно помітив цей коментар, але ви можете побачити мою відповідь про те, як використовувати сеанс із необробленим SQL.
jpmc26

180

Об'єкти сеансу алхімії SQL мають власний executeметод:

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

Усі ваші запити додатків повинні проходити через об'єкт сеансу, незалежно від того, чи є вони необробленими SQL чи ні. Це забезпечує правильне керування запитами трансакцією , яка дозволяє виконувати чи запити декількох запитів в одному запиті як один блок. Якщо виходити за межі транзакції за допомогою двигуна або підключення, ви піддаєте набагато більший ризик виникнення тонких, можливо важких для виявлення помилок, які можуть залишити вас зіпсованими даними. Кожен запит повинен бути пов’язаний лише з однією транзакцією, і використання db.sessionцього гарантує, що це стосується вашої заявки.

Також врахуйте, що executeрозроблено для параметризованих запитів . Використовуйте параметри, як :valу прикладі, для будь-яких входів у запит, щоб захистити себе від атак на ін'єкцію SQL. Ви можете надати значення для цих параметрів, передавши a dictяк другий аргумент, де кожен ключ - це ім'я параметра, як воно відображається в запиті. Точний синтаксис самого параметра може бути різним залежно від вашої бази даних, але всі основні реляційні бази даних підтримують їх у певній формі.

Якщо припустити , що це SELECTзапит, це поверне итератор з RowProxyоб'єктів.

Ви можете отримати доступ до окремих стовпців різними методами:

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

Особисто я вважаю за краще конвертувати результати в 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)

Якщо ви не використовуєте розширення Flask-SQLAlchemy, ви все одно можете легко використовувати сеанс:

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

A Select поверне ResultProxy.
Алан Б

@AlanB Так. Я погано вибрав слова, коли я назвав це послідовністю, маючи на увазі, що він реалізує протокол послідовностей. Я виправив і уточнив. Дякую.
jpmc26

@ jpmc26 повинен закрити сеанс після виконання запиту, як db.session.close ()? І чи все ще матиме переваги об'єднання з'єднань?
ravi malhotra

58

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

1
Посилання на документи sqlalchemy здається застарілим. Це нещодавніше: docs.sqlalchemy.org/en/latest/core/…
Карл

1
Чи можу я запитати, чому ми використовуємо ==?
Нам Г ВУ

1
@Jake Berger велике спасибі за вас Я майже витрачав день на пошуки цієї відповіді. Я просто виконував sql без перетворення в текст. Це було помилкою, коли у нас є% студентів%. Великі оплески за вашу відповідь.
Суреш Кумар

1
@ NamGVU, оскільки, як і в більшості мов програмування, =зазвичай зарезервовано для призначення значення; тоді ==як зарезервовано для порівняння значень
Джейк Бергер

2
@JakeBerger У вас є посилання на це? SQL - це не така мова, і судячи з документів SQLAlchemy, це не так.
johndodo

36

Ви можете отримати результати SELECT SQL запитів, використовуючи from_statement()та text()як показано тут . Вам не доведеться поводитися з кортежами таким чином. Як приклад для класу Userз назвою таблиці usersви можете спробувати,

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

виконує, <sql here>але не виконує його, якщо ви не перебуваєте в autocommitрежимі. Отже, вставки та оновлення не відображатимуться в базі даних.

Щоб виконати зміни після змін, зробіть це

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

2

Це спрощена відповідь про те, як запустити SQL-запит з Flask Shell

По-перше, нанесіть на карту модуль (якщо ваш модуль / додаток керує management.py у головній папці та ви перебуваєте в операційній системі UNIX), запустіть:

export FLASK_APP=manage

Виконати оболонку колби

flask shell

Імпортуйте те, що нам потрібно:

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

Запустіть свій запит:

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

Для цього використовується підключення до бази даних, яке наразі має додаток.


0

Ви намагалися використовувати connection.execute(text( <sql here> ), <bind params here> )та прив'язувати параметри, як описано в документах ? Це може допомогти вирішити багато проблем із форматуванням параметрів та продуктивністю. Можливо, помилка шлюзу - це тайм-аут? Параметри прив'язки, як правило, роблять складні запити істотно швидшими.


2
згідно з документами , так і має бути connection.execute(text(<sql here>), <bind params> ). bind paramsНЕ повинен бути в text(). подача параметрів прив'язки до методу Execute ()
Джейк Бергер

Посилання Джейка розірвана. Я думаю, що це URL-адреса, яка є актуальною зараз: docs.sqlalchemy.org/en/latest/core/…
code_dredd

-1

Якщо ви хочете , щоб уникнути кортежі, інший шлях, викликаючи first, oneабо allметоди:

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

assert query.first().name == "Welcome to my blog"
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.