SQLAlchemy: двигун, з'єднання та різниця сеансів


135

Я використовую SQLAlchemy і є, по крайней мере , три особи: engine, sessionі connection, які мають executeметод, тому , якщо я , наприклад , хочу , щоб вибрати всі записи з tableя можу зробити це

engine.execute(select([table])).fetchall()

і це

connection.execute(select([table])).fetchall()

і навіть це

session.execute(select([table])).fetchall()

- результати будуть однаковими.

Як я розумію, якщо хтось використовує engine.executeйого, створює connection, відкривається session(Алхімія піклується про це за вас) і виконує запит. Але чи існує глобальна різниця між цими трьома способами виконання такого завдання?


Я думаю, що ваша відповідь тут: hackersandslackers.com/…
SeF

Відповіді:


123

Однорядковий огляд:

Поведінка execute()таке ж у всіх випадках, але вони 3 різних методів, в Engine, Connectionі Sessionкласи.

Що саме таке execute():

Для розуміння поведінки execute()нам потрібно заглянути в Executableклас. Executable- це суперклас для всіх типів об'єктів "оператор", включаючи select (), delete (), update (), insert (), text () - найпростішими можливими словами, a Executable- це конструкція SQL-виразів, підтримувана в SQLAlchemy.

У всіх випадках execute()метод приймає текст SQL або побудований вираз SQL, тобто будь-яка з різноманітності конструкцій вираження SQL, що підтримуються в SQLAlchemy, і повертає результати запитів (a ResultProxy- Обертає об'єкт DB-APIкурсору для забезпечення більш легкого доступу до стовпців рядків.)


Для подальшого уточнення (лише для концептуальної роз'яснення, а не для рекомендованого підходу) :

На додаток до Engine.execute()(безз'єднаному виконанню), Connection.execute()і Session.execute(), також можна використовувати execute()безпосередньо на будь-якій Executableконструкції. ExecutableКлас має свою власну реалізацію execute()- по офіційній документації, один рядок опису про те , що execute()робить « Compile і виконати цеExecutable ». У цьому випадку нам потрібно явно зв’язати Executable(конструкцію вираження SQL) з Connectionоб'єктом або, Engineоб'єктом (який неявно отримує Connectionоб’єкт), тож execute()буде відомо, де його виконати SQL.

Наступний приклад це наочно демонструє - З огляду на таблицю, як показано нижче:

from sqlalchemy import MetaData, Table, Column, Integer

meta = MetaData()
users_table = Table('users', meta,
    Column('id', Integer, primary_key=True),
    Column('name', String(50)))

Явне виконання, тобто Connection.execute()передача тексту SQL або побудованого вираження SQL execute()методу Connection:

engine = create_engine('sqlite:///file.db')
connection = engine.connect()
result = connection.execute(users_table.select())
for row in result:
    # ....
connection.close()

Явне виконання без підключення, тобто Engine.execute()передача тексту SQL або побудований вираз SQL безпосередньо execute()методу Engine:

engine = create_engine('sqlite:///file.db')
result = engine.execute(users_table.select())
for row in result:
    # ....
result.close()

Неявне виконання, тобто Executable.execute()- також є беззв'язним і викликає execute()метод методу Executable, тобто викликає execute()метод безпосередньо на SQLконструкцію вираження (екземпляр Executable) самого себе.

engine = create_engine('sqlite:///file.db')
meta.bind = engine
result = users_table.select().execute()
for row in result:
    # ....
result.close()

Примітка: Наведений приклад неявного виконання з метою уточнення - такий спосіб виконання вкрай не рекомендується - згідно з документами :

"Неявне виконання" - це дуже стара модель використання, яка в більшості випадків є більш заплутаною, ніж корисною, і її використання не перешкоджає. Обидві моделі, схоже, заохочують надмірне використання доцільних "скорочень" в дизайні додатків, що призводить до проблем згодом.


Ваші запитання:

Як я розумію, якщо хтось використовує engine.execute, він створює з'єднання, відкриває сеанс (Алхімія піклується про вас) і виконує запит.

Ви маєте право на частину, "якщо хтось використовує engine.executeїї, створює connection", але не для "відкривається session(Алхімія про це піклується про вас) і виконує запит" - Використання Engine.execute()і Connection.execute()(майже) одне і те ж, формально Connectionоб'єкт створюється неявно , а в пізнішому випадку ми явно це інстанціюємо. Що насправді відбувається в цьому випадку:

`Engine` object (instantiated via `create_engine()`) -> `Connection` object (instantiated via `engine_instance.connect()`) -> `connection.execute({*SQL expression*})`

Але чи існує глобальна різниця між цими трьома способами виконання такого завдання?

У шарі DB це точно те саме, що всі вони виконують SQL (текстовий вираз або різні конструкції вираження SQL). З точки зору програми, є два варіанти:

  • Пряме виконання - за допомогою Engine.execute()абоConnection.execute()
  • Використання sessions- ефективно обробляє транзакції як єдиний блок-оф-роботи, з легкістю через session.add(), session.rollback(), session.commit(), session.close(). Це спосіб взаємодії з БД у випадку ORM, тобто відображених таблиць. Надає identity_map для миттєвого отримання вже доступних або новостворених / доданих об’єктів під час одного запиту.

Session.execute()врешті-решт використовує Connection.execute()метод виконання операторів для виконання оператора SQL. Використання Sessionоб'єкта є рекомендованим способом взаємодії програми з базою даних SQLAlchemy ORM.

Уривок із документів :

Важливо зазначити, що при використанні ORQ SQLAlchemy до цих об'єктів взагалі не можна отримати доступ; натомість об’єкт Session використовується як інтерфейс до бази даних. Однак для програм, побудованих на основі прямого використання текстових SQL-висловлювань та / або конструкцій вираження SQL без участі служб управління вищим рівнем ORM, двигун та з'єднання є королем (і королевою?) - читайте далі.


Слово «без зв’язку» означає, що не створюється зв’язок, що відповідно до відповіді Ніла - це не так.
Атом

111

Відповідь Набіля охоплює безліч деталей і є корисною, але я вважав це незрозумілим для наслідування. Оскільки це зараз перший результат Google для цієї проблеми, я додаю своє розуміння для майбутніх людей, які знайшли це питання:

Запуск .execute ()

Як зазначають ОП та Набелл Ахмед, виконуючи рівнину SELECT * FROM tablename, різниці в наданому результаті немає.

Відмінності між цими трьома об'єктами дійсно стають важливими в залежності від контексту , що SELECTвикористовується оператор або, частіше за все , коли ви хочете зробити інші речі , як INSERT, DELETEі т.д.

Коли використовувати двигун, з'єднання, сесію взагалі

  • Двигун - це об'єкт найнижчого рівня, використовуваний SQLAlchemy. Він підтримує пул з'єднань, доступних для використання, коли програма потребує спілкування з базою даних. .execute()є зручним методом, який спочатку дзвонить conn = engine.connect(close_with_result=True)і потім conn.execute(). Параметр close_with_result означає, що з'єднання закривається автоматично. (Я трохи перефразовуючи вихідний код, але по суті правда). редагувати: Ось вихідний код для engine.execute

    Ви можете використовувати двигун для виконання необробленого SQL.

    result = engine.execute('SELECT * FROM tablename;')
    #what engine.execute() is doing under the hood
    conn = engine.connect(close_with_result=True)
    result = conn.execute('SELECT * FROM tablename;')
    
    #after you iterate over the results, the result and connection get closed
    for row in result:
        print(result['columnname']
    
    #or you can explicitly close the result, which also closes the connection
    result.close()

    Це висвітлено в документах під основним використанням .

  • З'єднання - це те, що насправді виконує роботу із виконанням SQL-запиту. Ви повинні робити це щоразу, коли ви хочете більшого контролю над атрибутами з'єднання, коли він закривається і т. Д. Наприклад, дуже імпортним прикладом цього є транзакція , яка дозволяє вам вирішити, коли здійснити зміни в базі даних. При звичайному використанні зміни автоматично вводяться в дію. Використовуючи транзакції, ви могли (наприклад) запустити кілька різних SQL-операторів, і якщо щось піде не так з одним із них, ви можете скасувати всі зміни відразу.

    connection = engine.connect()
    trans = connection.begin()
    try:
        connection.execute("INSERT INTO films VALUES ('Comedy', '82 minutes');")
        connection.execute("INSERT INTO datalog VALUES ('added a comedy');")
        trans.commit()
    except:
        trans.rollback()
        raise

    Це дозволить вам скасувати обидва зміни, якщо одна не вдалася, наприклад, якщо ви забули створити таблицю журналу даних.

    Тож якщо ви виконуєте необроблений SQL-код і вам потрібен контроль, використовуйте з'єднання

  • Сесії використовуються для аспекту управління об'єктними відносинами (ORM) SQLAlchemy (адже ви можете бачити це з того, як вони імпортуються:) from sqlalchemy.orm import sessionmaker. Вони використовують підключення та транзакції під кришкою для запуску автоматично створених операторів SQL. .execute()- це функція зручності, яка переходить до того, до чого пов'язаний сеанс (як правило, двигун, але може бути з'єднанням).

    Якщо ви використовуєте функцію ORM, використовуйте сеанс; якщо ви робите лише прямі запити SQL, не пов'язані з об'єктами, вам, ймовірно, краще використовувати з'єднання безпосередньо.


1
Чи не слід вставляти заяви укладені у подвійні лапки ""?
мінчау

2
@mingchau Так, ви праві, мої єдині цитати заважали б один одному, подвійні цитати набагато простіше уникнути цього питання. Оновлено.
Ніл

З огляду на створений сеанс, як пов’язаний мій сеанс із моїм підключенням PostgreSQL?
Raju yourPepe

@RajuyourPepe my_session.connection(). Документи: docs.sqlalchemy.org/en/13/orm / ... .
Ніл

Серйозно? Об'єкт 'Session' не має атрибута 'connect' ", це те, що я знайшов
Raju yourPepe

0

Ось приклад запуску DCL (Мова управління даними), такий як GRANT

def grantAccess(db, tb, user):
  import sqlalchemy as SA
  import psycopg2

  url = "{d}+{driver}://{u}:{p}@{h}:{port}/{db}".\
            format(d="redshift",
            driver='psycopg2',
            u=username,
            p=password,
            h=host,
            port=port,
            db=db)
  engine = SA.create_engine(url)
  cnn = engine.connect()
  trans = cnn.begin()
  strSQL = "GRANT SELECT on table " + tb + " to " + user + " ;"
  try:
      cnn.execute(strSQL)
      trans.commit()
  except:
      trans.rollback()
      raise
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.