Армин Роначер. SQLAlchemy во Flask, 2012

Перевод статьи: SQLAlchemy in Flask

Автор: Армин Роначер (Armin Ronacher)

Многие люди предпочитают использовать SQLAlchemy для доступа к базам данных. В этом случае для написания приложений на Flask больше подходят пакеты вместо модулей, так как в этом случае можно поместить модели в отдельный модуль (Большие приложения). Хотя это и не является необходимым, это имеет смысл.

Есть четыре обычных способа использования SQLAlchemy. Остановимся на каждом из них подробнее:

1. Расширение Flask-SQLAlchemy

Поскольку SQLAlchemy - это обобщённый слой абстракции над базами данных и объектно-реляционное отображение, требующее предварительной настройки, существует расширение Flask, делающее всё необходимое за вас. Если нужно быстро начать работу, рекомендуем воспользоваться им.

Расширение Flask-SQLAlchemy можно скачать из PyPI.

2. Declarative

Расширение declarative в SQLAlchemy - это один из наиболее частых способов использования SQLAlchemy. Оно позволяет вам определять таблицы и модели одновременно, примерно так, как это делается в Django. В дополнение к следующему тексту рекомендуется обратиться к официальной документации по расширению declarative.

Вот пример модуля database.py для приложения:

from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()

def init_db():
    # Здесь нужно импортировать все модули, где могут быть определены модели,
    # которые необходимым образом могут зарегистрироваться в метаданных. 
    # В противном случае их нужно будет импортировать до вызова init_db()
    import yourapplication.models
    Base.metadata.create_all(bind=engine)

Для определения собственных моделей наследуйте от класса Base, который создан вышеприведённым кодом. Если вы удивлены, почему в этом примере не нужно заботиться о потоках (как мы делали в примере для SQLite3 с объектом g выше), то это потому что SQLAlchemy делает это самостоятельно при помощи scoped_session.

Чтобы использовать SQLAlchemy в приложении декларативным образом, необходимо поместить в модуль вашего приложения следующий код. Flask автоматически удалит сеанс базы данных в конце запроса:

from yourapplication.database import db_session

@app.teardown_request
def shutdown_session(exception=None):
    db_session.remove()

Вот пример модели (поместите его, например, в models.py):

from sqlalchemy import Column, Integer, String
from yourapplication.database import Base

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(50), unique=True)
    email = Column(String(120), unique=True)

    def __init__(self, name=None, email=None):
        self.name = name
        self.email = email

    def __repr__(self):
        return '<User %r>' % (self.name)

Для создания базы данных можно воспользоваться функцией init_db:

>>> from yourapplication.database import init_db
>>> init_db()

Вот так можно добавить новые записи в базу данных:

>>> from yourapplication.database import db_session
>>> from yourapplication.models import User
>>> u = User('admin', 'admin@localhost')
>>> db_session.add(u)
>>> db_session.commit()

Пример запроса:

>>> User.query.all()
[<User u'admin'>]
>>> User.query.filter(User.name == 'admin').first()
<User u'admin'>

3. Ручное объектно-реляционное отображение

Ручное объектно-реляционное отображение имеет некоторые преимущества и недостатки по сравнению с декларативным подходом, рассмотренным выше. Главное отличие заключается в том, что таблицы и классы определяются раздельно, а затем создаётся их взаимное отображение. Этот подход более гибок, однако и более трудоёмок. В целом он работает подобно декларативному подходу, поэтому убедитесь в том, что поделили ваше приложение на несколько модулей в пакете.

Вот пример модуля database.py:

from sqlalchemy import create_engine, MetaData
from sqlalchemy.orm import scoped_session, sessionmaker

engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
metadata = MetaData()
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
def init_db():
    metadata.create_all(bind=engine)

Как и в декларативном случае, вам необходимо закрывать сеанс после каждого запроса. Поместите следующие строки в модуль вашего приложения:

from yourapplication.database import db_session

@app.teardown_request
def shutdown_session(exception=None):
    db_session.remove()

Вот пример таблицы и модели (поместите их в models.py):

from sqlalchemy import Table, Column, Integer, String
from sqlalchemy.orm import mapper
from yourapplication.database import metadata, db_session

class User(object):
    query = db_session.query_property()

    def __init__(self, name=None, email=None):
        self.name = name
        self.email = email

    def __repr__(self):
        return '<User %r>' % (self.name)

users = Table('users', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(50), unique=True),
    Column('email', String(120), unique=True)
)
mapper(User, users)

Запрос и вставка записей делается точно так же, как в примере выше.

4. Слой абстракции над SQL

Если вы хотите использовать только слой абстракции к базам данных (и SQL), вам потребуется только объект engine:

from sqlalchemy import create_engine, MetaData

engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True)
metadata = MetaData(bind=engine)

Теперь можно объявить таблицы в коде, как в примере выше или автоматически загрузить их:

users = Table('users', metadata, autoload=True)

Чтобы вставить данные, вы можете воспользоваться методом insert. Прежде чем совершить транзакцию, необходимо сначала получить подключение:

>>> con = engine.connect()
>>> con.execute(users.insert(), name='admin', email='admin@localhost')

SQLAlchemy автоматически подтвердит транзакцию.

Для выполнения запросов можно воспользоваться напрямую объектом engine, либо использовать подключение:

>>> users.select(users.c.id == 1).execute().first()
(1, u'admin', u'admin@localhost')

С результатом запроса можно обращаться как со словарём:

>>> r = users.select(users.c.id == 1).execute().first()
>>> r['name']
u'admin'

В метод execute() можно также передавать строки с выражениями SQL:

>>> engine.execute('select * from users where id = :1', [1]).first()
(1, u'admin', u'admin@localhost')

За более подробной информацией о SQLAlchemy обратитесь к вебсайту.

5. Примечания переводчика

В сети можно найти учебник по использованию SQLAlchemy: SQLAlchemy - викиучебник.

Этот и другие переводы можно найти на сайте проекта перевода документации по Flask. Автор проекта - Виталий Кузьмин aka ferm32.

Написать автору перевода