Расширение для поддержки SQLAlchemy в Flask
Flask-SQLAlchemy удобен в использовании, невероятно прост для базовых приложений и легко расширяется для более крупных приложений. Для получения полного руководства ознакомьтесь с документацией по API для класса SQLAlchemy.
В общем случае наличия одного приложения Flask все что нужно для начала работы. Создайте приложение Flask, загрузите выбранную конфигурацию и затем создате объект SQLAlchemy, передав ему ссылку на приложение.
После создания этот объект содержит все функции и хелперы из sqlalchemy и sqlalchemy.orm. Кроме того, он предоставляет класс Model, который является декларативной базой, которую можно использовать для объявления моделей:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
def __repr__(self):
return '<User %r>' % self.username
Для создания исходной базы данных просто импортируйте объект db
из интерактивной оболочки Python и запустите метод
SQLAlchemy.create_all() для создания таблиц и базы данных:
from yourapplication import db
db.create_all()
Теперь, чтобы создать несколько пользователей выполните следующее:
from yourapplication import User
admin = User(username='admin', email='admin@example.com')
guest = User(username='guest', email='guest@example.com')
Но они еще не в базе данных, поэтому давайте отправим их туда:
db.session.add(admin)
db.session.add(guest)
db.session.commit()
Доступ к данным в базе данных прост:
User.query.all()
# [<User u'admin'>, <User u'guest'>]
User.query.filter_by(username='admin').first()
# <User u'admin'>
Обратите внимание, как мы никогда не определяли метод __init__
в классе User. Это потому, что SQLAlchemy добавляет
неявный конструктор ко всем классам модели, который принимает аргументы ключевых слов для всех своих столбцов и
отношений. Если вы по какой-либо причине решите переопределить конструктор, обязательно продолжайте принимать
**kwargs
и вызывайте супер-конструктор с этими **kwargs
, чтобы сохранить это поведение:
class Foo(db.Model):
# ...
def __init__(**kwargs):
super(Foo, self).__init__(**kwargs)
#...
SQLAlchemy подключается к реляционным базам данных, и в этом отношении реляционные базы данных действительно хороши. Таким образом, у нас будет пример приложения, которое использует две таблицы, которые имеют отношение друг к другу:
from datetime import datetime
class Post(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80), nullable=False)
body = db.Column(db.Text, nullable=False)
pub_date = db.Column(db.DateTime, nullable=False,
default=datetime.utcnow)
category_id = db.Column(db.Integer, db.ForeignKey('category.id'),
nullable=False)
category = db.relationship('Category',
backref=db.backref('posts', lazy=True))
def __repr__(self):
return '<Post %r>' % self.title
class Category(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
def __repr__(self):
return '<Category %r>' % self.name
Сначала давайте создадим несколько объектов:
py = Category(name='Python')
Post(title='Hello Python!', body='Python is pretty cool', category=py)
p = Post(title='Snakes', body='Ssssssss')
py.posts.append(p)
db.session.add(py)
Как видите, нет необходимости добавлять объекты Post в сеанс. Поскольку Категория является частью сеанса, все объекты,
связанные с ней посредством отношений, также будут добавлены. Не имеет значения, вызывается ли db.session.add()
до
или после создания этих объектов. Связывание также может быть выполнено с любой стороны отношения, поэтому можно
создать сообщение с категорией или добавить его в список сообщений этой категории.
Давайте посмотрим на сообщения:
py.posts
# [<Post 'Hello Python!'>, <Post 'Snakes'>]
Несмотря на то, что отложенная загрузка отношения быстрая, она может легко стать узким местом, когда вы в конечном итоге инициируете дополнительные запросы в цикле для нескольких объектов. В этом случае SQLAlchemy позволяет переопределить стратегию загрузки на уровне запроса. Если вам нужен один запрос для загрузки всех категорий и их сообщений, вы можете сделать это следующим образом:
from sqlalchemy.orm import joinedload
query = Category.query.options(joinedload('posts'))
for category in query:
print(category, category.posts)
# <Category u'Python'> [<Post u'Hello Python!'>, <Post u'Snakes'>]
Если вы хотите получить объект запроса для этого отношения, вы можете сделать это с помощью with_parent()
.
Давайте условие в наш запрос:
Post.query.with_parent(py).filter(Post.title != 'Snakes').all()
# [<Post 'Hello Python!'>]
Единственное, что вам нужно знать по сравнению с простым SQLAlchemy:
session
metadata
engine
SQLAlchemy.create_all()
и SQLAlchemy.drop_all()
для создания и удаления таблиц в соответствии с моделями.Декларативный базовый класс Model
ведет себя как обычный класс Python, но к нему прикреплен атрибут query
,
который можно использовать для запроса модели. (Model
и BaseQuery
)
Вы должны выполнить session.commit()
, но вам не нужно удалять его в конце запроса, Flask-SQLAlchemy
сделает это за вас.