Библиотека сайта rus-linux.net
SQLAlchemy
Глава 20 из книги "Архитектура приложений с открытым исходным кодом", том 2.Оригинал: SQLAlchemy
Автор: Michael Bayer
Перевод: А. Панин
20.3. Использование DBAPI
В основании SQLAlchemy находится подсистема для взаимодействия с базами данных посредством DBAPI. Сам по себе DBAPI представлен не отдельной библиотекой, а исключительно спецификацией. Поэтому реализации спецификации DBAPI доступны для определенных целевых баз данных, таких, как MySQL или PostgreSQL или в качестве альтернативы для определенных адаптеров для баз данных, не совместимых с DBAPI, таких, как ODBC и JDBC.
Использование DBAPI приводит к двум сложностям. Первая сложность заключается в необходимости предоставления простого и полнофункционального фасадного класса для рудиментарных шаблонов использования DBAPI. Вторая сложность заключается в необходимости обработки значительных отличий специфических реализаций DBAPI, а также используемых систем баз данных.
Система диалектов
connection = dbapi.connect(user="user", pw="pw", host="host") cursor = connection.cursor() cursor.execute("select * from user_table where name=?", ("jack",)) print "Результирующие столбцы:", [desc[0] for desc in cursor.description] for row in cursor.fetchall(): print "Строка:", row cursor.close() connection.close()
В рамках SQLAlchemy реализован фасадный класс для классического взаимодействия с DBAPI. Точкой входа этого фасадного класса является вызов create_engine
, с помощью которого устанавливается соединение и собирается конфигурационная информация. В качестве результата выполнения вызова возвращается экземпляр класса Engine
. Этот объект представляет только способ осуществления запроса через DBAPI, причем последний никогда непосредственно не раскрывается.
Engine
предоставляет интерфейс, известный под названием "интерфейс явного исполнения запросов" ("implicit execution interface"). Работа по созданию и закрытию соединения с базой данных и курсора посредством DBAPI выполняется незаметно для разработчика:
engine = create_engine("postgresql://user:pw@host/dbname") result = engine.execute("select * from table") print result.fetchall()
Connection
, позволяющий явно выполнять этапы процесса соединения с базой данных посредством DBAPI:
conn = engine.connect() result = conn.execute("select * from table") print result.fetchall() conn.close()
Возвращаемый методом execute
класса Engine
или Connection
результат называется ResultProxy
и предоставляет интерфейс, аналогичный интерфейсу курсора в DBAPI, но с большим набором функций. Объекты Engine
, Connection
и ResultProxy
связаны с модулем DBAPI и являются экземплярами определенного соединения DBAPI и определенного курсора DBAPI соответственно.
На заднем плане объект Engine
ссылается на объект, называемый Dialect
. Dialect
является абстрактным классом, для которого существует множество реализаций, причем каждая из этих реализаций предназначена для работы с определенной комбинацией DBAPI и базы данных. Объект Connection
, создаваемый на стороне объекта Engine
, будет ссылаться на этот объект Dialect
при принятии всех решений, которые могут варьироваться в зависимости от используемых DBAPI и базы данных.
После создания объект Connection
будет создавать и поддерживать рабочее соединение DBAPI из репозитория, известного, как Pool
, который также ассоциирован с объектом Engine
. Репозиторий Pool
ответственен за создание новых соединений DBAPI и обычно за сохранение их в расположенном в памяти пуле для периодического использования.
В процессе исполнения запроса объектом Connection
создается дополнительный объект с именем ExecutionContext
. Этот объект существует с момента исполнения запроса в течение периода существования объекта ResultProxy
. Он также может быть доступен как специфический подкласс для некоторых комбинаций DBAPI и баз данных.
Рисунок 20.2 иллюстрирует все эти объекты и их взаимоотношения с другими объектами, а также с компонентами DBAPI.
Рисунок 20.2: API объектов Engine, Connection, ResultProxy
Обработка различий интерфейсов DBAPI
Перед рассмотрением задачи обработки различий интерфейсов DBAPI, давайте для начала рассмотрим суть существующей проблемы. Спецификация DBAPI, на данный момент второй версии, написана в форме наборов объявлений API, которые позволяют реализовывать значительно отличающиеся по поведению интерфейсы, а также оставляют большое количество недокументированных областей. В результате существующие реализации DBAPI демонстрируют значительные отличия в некоторых областях, включая возможность или невозможность передачи строк языка Python в кодировке Unicode; способ получения "последнего добавленного идентификатора", являющегося автоматически генерируемым первичным ключом, после выполнения запроса INSERT; а также способ указания и интерпретации граничных значений. Эти интерфейсы также ведут себя индивидуально в зависимости от используемых типов данных, в ситуациях, когда производится обработка бинарных данных, точных числовых данных, дат, логических данных, а также строк в кодировке Unicode.
SQLAlchemy решает эту проблему, допуская различия в классах Dialect
и ExecutionContext
путем использования множества уровней подклассов. Рисунок 20.3 иллюстрирует отношение между объектами Dialect
и ExecutionContext
в случае использования диалекта psycopg2. Класс PGDialect
реализует специфичные для базы данных PostgreSQL возможности, такие, как поддержка типа данных ARRAY и каталогов схем; класс PGDialect_psycopg2
реализует возможности, специфичные для реализации DBAPI psycopg2, включающие обработчики строк в кодировке Unicode и поддержку управления курсором на стороне сервера базы данных.
Рисунок 20.3: Простая иерархия классов Dialect/ExecutionContext
Вариант описанного выше шаблона проектирования оправдывает себя при работе с реализацией DBAPI, поддерживающей множество баз данных. Примерами таких реализаций являются pyodbc, которая может взаимодействовать с неограниченным количеством баз данных посредством ODBC и zxjdbc, предназначенная только для использования совместно с языком Jython и работающая с JDBC. Описанное выше отношение классов реализуется путем использования смешанного класса из пакета sqlalchemy.connectors
, который реализует особенности работы интерфейса DBAPI, свойственные для множества баз данных. Рисунок 20.4 иллюстрирует стандартные функции класса sqlalchemy.connectors.pyodbc
, разделяемые между pyodbc-специфичными диалектами для баз данных MySQL и Microsoft SQL Server.
Рисунок 20.4: Стандартные функции DBAPI, разделяемые между иерархиями диалектов
Объекты Dialect
и ExecutionContext
предоставляют возможность для описания каждого взаимодействия с базой данных и DBAPI, включая то, как должны форматироваться аргументы функции соединения, а также как должны обрабатываться специальные данные в процессе исполнения запроса. Объект Dialect
также является фабрикой для компиляции синтаксических конструкций языка SQL, которая осуществляет корректное оформление SQL-запроса для целевой базы данных, а также преобразования типов объектов данных в соответствии с тем, как объекты данных языка Python должны упаковываться и распаковываться при использовании целевых интерфейса DBAPI и базы данных.
Продолжение статьи: Описание схемы