У меня есть следующая настройка, для которой в session.query () SqlAlchemy возвращает устаревшие данные:
Веб-приложение, работающее на Flask с супервизором Gunicorn +. одна из служб составлена таким образом:
app.py:
@app.route('/api/generatepoinvoice', methods=["POST"])
@auth.login_required
def generate_po_invoice():
try:
po_id = request.json['po_id']
email = request.json['email']
return jsonify(response=POInvoiceGenerator.get_invoice(po_id, email))
except Exception as ex:
app.logger.error("generate_po_invoice(): " + ex.message)
в другой папке у меня есть материалы, связанные с базой данных:
DatabaseModels (папка)
| -> Model.py
| -> Connection.py
это то, что содержится в файле connection.py:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine(DB_BASE_URI, isolation_level = "READ COMMITTED")
Session = scoped_session(sessionmaker(bind=engine))
session = Session()
Base = declarative_base()
и это отрывок из файла model.py:
from DatabaseModels.Connection import Base
from sqlalchemy import Column, String, etc...
class Po(Base):
__tablename__ = 'PLC_PO'
id = Column("POId", Integer, primary_key=True)
code = Column("POCode", String(50))
etc...
Тогда у меня есть еще один файл POInvoiceGenerator.py который содержит вызов базы данных для получения некоторых данных:
import DatabaseModels.Connection as connection
import DatabaseModels.model as model
def get_invoice(po_code, email):
try:
po_code = po_code.strip()
PLCConnection.session.expire_all()
po = connection.session.query(model.Po).filter(model.Po.code == po_code).first()
except Exception as ex:
logger.error("get_invoice(): " + ex.message)
при последующих обращениях пользователей к этой службе иногда я начинаю получать такие ошибки, как: не удалось найти данные в базе данных для этого конкретного кода и т. д. Например, если данные устарели и так далее.
Мой первый подход заключался в том, чтобы добавить изоляцию_level = "READ COMMITTED" в объявление движка, а затем создать сеанс с заданной областью действия, но чтение устаревших данных продолжает добавляться.
Есть ли кто-нибудь, кто имел какое-либо представление о том, что моя установка неверна (сеанс и модель повторно используются в нескольких методах и файлах)
Заранее спасибо.
🤔 А знаете ли вы, что...
Python популярен в анализе данных и машинном обучении с помощью библиотеки scikit-learn.
даже если решение, на который указывает @TonyMountax, кажется действительным и заставил меня обнаружить то, чего я не знал о SqlAlchemy, в конце концов я выбрал что-то другое.
Я понял, что соединение, установленное SqlAlchemy, было надежным, поскольку оно создавалось из пула соединений каждый раз, это каким-то образом приводило к устареванию данных.
Я добавил в свой код NullPool:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.pool import NullPool
engine = create_engine(DB_URI, isolation_level = "READ COMMITTED", poolclass=NullPool)
Session = scoped_session(sessionmaker(bind=engine))
session = Session()
а затем я вызываю закрытие сеанса для каждого запроса, который я делаю:
session.query("some query..")
session.close()
это заставит SqlAlchemy каждый раз создавать новое соединение и получать свежие данные из базы данных.
Надеюсь, что это правильный способ его использования, и он может быть полезен кому-то другому.
То, как вы создаете экземпляры соединений с базой данных, означает, что они повторно используются для следующего запроса, и у них есть какое-то состояние, оставшееся от предыдущего запроса. SQLAlchemy использует концепцию сеансов для взаимодействия с базой данных, так что ваши данные не изменяются резко в одном запросе, даже если вы выполняете один и тот же запрос дважды. Это имеет смысл, когда вы используете функции запросов ORM. Например, если вы дважды запросили len(User.friendlist)
в течение одного сеанса, но во время запроса был принят запрос на добавление в друзья, то он все равно будет показывать тот же номер в обоих местах.
Чтобы исправить это, вы должны настроить сеанс при первом запросе, а затем разорвать его, когда запрос будет завершен. Сделать это нетривиально, но есть хорошо зарекомендовавший себя проект, который уже делает это: Flask-SQLAlchemy. Это от Pallets, разработчиков самого Flask и Jinja2.