SQLAlchemy: создание и повторное использование сеанса
просто быстрый вопрос: SQLAlchemy переговоры о вызов sessionmaker() один раз, но называть получившееся Session() класс каждый раз, когда вам нужно поговорить с вашей БД. Для меня это означает, что второй я бы сделал свой первый session.add(x) или что-то подобное, я бы сначала сделать
from project import Session
session = Session()
то, что я делал до сих пор, было сделать звонок session = Session() в моей модели после а затем всегда импортировать один и тот же сеанс в любом месте моего приложения. Поскольку это веб-приложения это обычно означает то же самое (как один вид выполняется).
но в чем же разница? В чем недостаток использования одного сеанса все время против использования его для моей базы данных, пока моя функция не будет выполнена, а затем создать новый в следующий раз, когда я хочу поговорить с моей БД?
Я понимаю, что если я использую несколько потоков, каждый из них должен получить свой собственный сеанс. Но с помощью scoped_session(), Я уже убедился, что эта проблема не существует, не так ли?
пожалуйста уточнить, если мои предположения ошибочны.
3 ответов:
sessionmaker()Это завод, он там, чтобы поощрять размещение параметров конфигурации для создания новыхSessionобъекты в одном месте. Это необязательно, что можно просто позвонитьSession(bind=engine, expire_on_commit=False)в любое время Вы нужен новыйSession, за исключением того, что он многословен и избыточен, и я хотел остановить распространение мелких "помощников", которые каждый подходил к проблеме этой избыточности каким-то новым и более запутанным способом.так
sessionmaker()Это просто инструмент, который поможет вам создатьSessionобъекты, когда они вам нужны.Следующая часть. Я думаю, что вопрос в том, в чем разница между созданием нового
Session()в различных точках по сравнению с просто использованием одного до конца. Ответ, не очень много.Sessionявляется контейнером для всех объектов, которые вы положили в него, а затем он также отслеживает открытой сделки. В данный момент Вы звонитеrollback()илиcommit(), транзакция закончена, иSessionне имеет подключения к базе данных, пока она не будет вызвана чтобы снова выдать SQL. Ссылки, которые он содержит на ваши сопоставленные объекты, являются слабыми ссылками, при условии, что объекты чисты от ожидающих изменений, поэтому даже в этом отношенииSessionбудет опустошить себя обратно в совершенно новое состояние, когда ваше приложение теряет все ссылки на сопоставленные объекты. Если вы оставите его с его значением по умолчанию"expire_on_commit"установка, то все объекты истекли после фиксации. Если чтоSessionболтается в течение пяти или двадцати минут, и все виды вещей изменились в базе данных в следующий раз, когда вы используете его, он загрузит все совершенно новое состояние при следующем доступе к этим объектам, даже если они сидели в памяти в течение двадцати минут.в веб-приложениях, мы обычно говорим, Эй, почему бы вам не сделать новый
Sessionна каждый запрос, а не использовать один и тот же снова и снова. Эта практика гарантирует, что новый запрос начинается "чистым". Если некоторые объекты из предыдущего запроса еще не были собраны в мусор, и если, возможно, вы повернули выкл"expire_on_commit", возможно, какое-то состояние из предыдущего запроса все еще висит вокруг, и это состояние может быть даже довольно старым. Если вы будете осторожны, чтобы уйтиexpire_on_commitвключен и определенно позвонитьcommit()илиrollback()в конце запроса, то это нормально, но если вы начинаете с совершенно новогоSession, тогда даже нет никаких вопросов, что вы начинаете с чистого листа. Так что идея начинать каждый запрос с новогоSessionэто действительно просто самый простой способ убедиться, что вы начинаете новый, и сделать использованиеexpire_on_commitв значительной степени необязательно, так как этот флаг может нести много дополнительного SQL для операции, которая вызываетcommit()в середине серии операций. Не уверен, что это ответ на ваш вопрос.следующий раунд-это то, что вы упоминаете о многопоточности. Если ваше приложение многопоточное, мы рекомендуем убедиться, что
Sessionиспользуется локально...что-то.scoped_session()по умолчанию делает его локальным для текущего потока. В веб-приложении локальный запрос на самом деле еще лучше. Flask-SQLAlchemy фактически отправляет пользовательскую функцию "scope" вscoped_session()так что вы получите сеанс с областью запроса. Среднее приложение пирамиды вставляет сеанс в реестр" запрос". При использовании таких схем идея "создать новый сеанс по запросу" продолжает выглядеть как самый простой способ сохранить все прямо.
в дополнение к превосходному ответу 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()
вы можете создать сеанс с помощью db
db = SQLAlchemy(app) engine = db.engine Session = sessionmaker(engine) session = Session()
Comments