Мое приложение использует сеанс с областью действия и декларативный стиль SQLALchemy. Это веб-приложение, и многие вставки БД выполняются Celery
, планировщиком задач.
Как правило, при принятии решения о вставке объекта мой код может выполнять что-то в следующих строках:
from schema import Session
from schema.models import Bike
pk = 123 # primary key
bike = Session.query(Bike).filter_by(bike_id=pk).first()
if not bike: # no bike in DB
new_bike = Bike(pk, "shiny", "bike")
Session.add(new_bike)
Session.commit()
Проблема здесь в том, что, поскольку многие из них выполняются асинхронными рабочими, возможно, что один работает на полпути, хотя вставляет Bike
с id=123
, а другой проверяет его существование. В этом случае второй рабочий попытается вставить строку с тем же самым первичным ключом, и SQLAlchemy поднимет значение IntegrityError
.
Я не могу на всю жизнь найти хороший способ справиться с этой проблемой, кроме замены Session.commit()
для:
'''schema/__init__.py'''
from sqlalchemy.orm import scoped_session, sessionmaker
Session = scoped_session(sessionmaker())
def commit(ignore=False):
try:
Session.commit()
except IntegrityError as e:
reason = e.message
logger.warning(reason)
if not ignore:
raise e
if "Duplicate entry" in reason:
logger.info("%s already in table." % e.params[0])
Session.rollback()
И тогда везде у меня есть Session.commit
Теперь у меня есть schema.commit(ignore=True)
, где я не против, чтобы строка не была вставлена снова.
Для меня это кажется очень хрупким из-за проверки строки. Подобно FYI, когда создается IntegrityError
, он выглядит так:
(IntegrityError) (1062, "Duplicate entry '123' for key 'PRIMARY'")
Итак, конечно, основным ключом, который я вставлял, было что-то вроде Duplicate entry is a cool thing
, тогда я предполагаю, что могу пропустить IntegrityError
, которые на самом деле не были из-за дублирования первичных ключей.
Существуют ли какие-либо более эффективные подходы, которые поддерживают чистый подход SQLAlchemy, который я использую (в отличие от того, чтобы начинать выписывать инструкции в строках и т.д.).
Db - это MySQL (хотя для модульного тестирования я хотел бы использовать SQLite и не хотел бы препятствовать этой способности любыми новыми подходами).
Ура!