Подтвердить что ты не робот

SQLAlchemy: создание или повторное использование сеанса

Просто быстрый вопрос: SQLAlchemy говорит о, вызывающем sessionmaker() один раз, но вызывающий результирующий класс Session() каждый раз, когда вам нужно поговорить с вашей БД. Для меня это означает, что второй я бы сделал свой первый session.add(x) или что-то подобное, я бы сначала сделал

from project import Session
session = Session()

То, что я делал до сих пор, заключалось в том, чтобы сделать вызов session = Session() в моей модели один раз, а затем всегда импортировать один и тот же сеанс в любом месте приложения. Поскольку это веб-приложения, это обычно означает одно и то же (как одно представление выполняется).

Но где разница? В чем заключается недостаток использования одного сеанса все время от его использования для моего материала базы данных до тех пор, пока моя функция не будет выполнена, а затем создадим новую в следующий раз, когда я захочу поговорить с моей БД?

Я получаю, что если я использую несколько потоков, каждый должен получить свой собственный сеанс. Но используя scoped_session(), я уже убедился, что проблема не существует, не так ли?

Просьба пояснить, являются ли какие-либо из моих допущений неправильными.

4b9b3361

Ответ 1

sessionmaker() - это factory, чтобы поощрять размещение параметров конфигурации для создания новых объектов Session только в одном месте. Это необязательно, так как вы могли бы просто вызвать Session(bind=engine, expire_on_commit=False) в любое время, когда вам понадобится новый Session, за исключением того, что он многословный и избыточный, и я хотел остановить распространение мелкомасштабных "помощников", к которым каждый обратился к проблеме этой избыточности в какой-то новой и более запутанной форме.

Итак, sessionmaker() - это просто инструмент, который поможет вам создавать объекты Session, когда они вам понадобятся.

Следующая часть. Я думаю, что вопрос в том, какая разница между созданием нового Session() в разных точках и просто использованием всего этого. Ответ, не очень. Session - это контейнер для всех объектов, которые вы вставляете в него, а затем он также отслеживает открытую транзакцию. В настоящий момент вы вызываете rollback() или commit(), транзакция завершена, а Session не имеет подключения к базе данных, пока она не будет вызвана для повторного выпуска SQL. Ссылки, которые он связывает с вашими сопоставленными объектами, являются слабыми ссылками, если объекты чисты от ожидающих изменений, поэтому даже в этом случае Session снова выйдет из себя в совершенно новое состояние, когда ваше приложение теряет все ссылки на сопоставленные объекты. Если вы оставите его с настройкой по умолчанию "expire_on_commit", то все объекты истек после фиксации. Если этот Session зависает в течение пяти или 20 минут, и всевозможные вещи изменились в базе данных при следующем использовании, он будет загружать все новое состояние при следующем доступе к этим объектам, даже если они были сидя в памяти в течение двадцати минут.

В веб-приложениях мы обычно говорим: "Почему бы вам не сделать новый Session по каждому запросу, а не использовать один и тот же снова и снова. Эта практика гарантирует, что новый запрос начнется" чистым". Если некоторые объекты из предыдущего запроса еще не были собраны в мусор, и, возможно, вы отключили "expire_on_commit", возможно, некоторое состояние из предыдущего запроса все еще висит вокруг, и это состояние может быть даже довольно старым. Если вы стараетесь оставить expire_on_commit включенным и определенно называть commit() или rollback() по завершении запроса, то это хорошо, но если вы начинаете с нового Session, тогда нет даже вопроса, который вы начинаете чистку. Таким образом, идея начать каждый запрос с помощью нового Session - это просто самый простой способ убедиться, что вы начинаете свежие, и сделать использование expire_on_commit довольно необязательным, так как этот флаг может нести много лишних SQL для операции, которая вызывает commit() в середине последовательности операций. Не уверен, что это отвечает на ваш вопрос.

Следующий раунд - это то, о чем вы упоминаете о потоковом использовании. Если ваше приложение многопоточно, мы рекомендуем убедиться, что используемый Session локальный для... что-то. scoped_session() по умолчанию делает его локальным для текущего потока. В веб-приложении локальный запрос действительно даже лучше. Flask-SQLAlchemy фактически отправляет настраиваемую "функцию области видимости" на scoped_session(), чтобы получить сеанс с охватом запросов. Среднее приложение Pyramid вставляет сеанс в реестр "запрос". При использовании подобных схем идея "создать новый сеанс по запросу" по-прежнему выглядит как самый простой способ держать вещи в чистоте.

Ответ 2

В дополнение к отличному отвечу zzzeek, ​​вот простой рецепт, чтобы быстро создать отложенные сеансы с закрытыми сеансами:

from contextlib import contextmanager

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

@contextmanager
def db_session(db_url):
    """ Creates a context with an open SQLAlchemy session.
    """
    engine = create_engine(db_url, convert_unicode=True)
    connection = engine.connect()
    db_session = scoped_session(sessionmaker(autocommit=False, autoflush=True, bind=engine))
    yield db_session
    db_session.close()
    connection.close()

Использование:

from mymodels import Foo

with db_session("sqlite://") as db:
    foos = db.query(Foo).all()

Ответ 3

Вы можете создать сеанс, используя db

db = SQLAlchemy(app)
engine = db.engine
Session = sessionmaker(engine)
session = Session()