У нас есть несколько функциональных возможностей в нашем веб-приложении Flask, которое состоит из одного вызова функции, который вызывает много подфункций и делает много вещей за кулисами. Например, он добавляет (финансовые) транзакции в базу данных (MSSQL), записывает данные в журнальную таблицу в базе данных и изменяет свойства конкретных объектов, что приводит к изменению столбцов в определенных таблицах в нашей базе данных. Все это делается с помощью SQLAlchemy через объекты.
В новом подходе, из-за тестируемости и из-за того, что нам иногда нужно только отображать эти изменения без фактической фиксации их в базе данных, эти функции возвращают составной объект Python, который содержит все измененные объекты.
Таким образом, вместо фиксации изменений базы данных внутри функции и подфункций, мы заставляем их возвращать измененные объекты, поэтому мы можем решить показать или сохранить их вне основной функции.
Таким образом, функция main возвращает составной объект со всеми этими измененными объектами, и вне основной функции мы добавляем эти измененные объекты в наш сеанс SQLAlchemy и фиксируем сеанс в базе данных. (или, если нам просто нужно отобразить информацию, мы не добавляем и не фиксируем). То, как мы это делаем, состоит в том, что у составного результирующего объекта есть функция save_to_session()
, которая сохраняет наши измененные объекты с помощью операции bulk_save_objects()
SQLAlchemy:
if result:
result.save_to_session(current_app.db_session)
current_app.db_session.commit()
def save_to_session(self, session):
session.bulk_save_objects(self.adminlog)
...
Этот новый подход привел к ошибке, которую мы не ожидали в строке current_app.db_session.commit()
. Кажется, что в конце процесса, когда мы добавляем возвращенные объекты в сеанс и пытаемся зафиксировать сеанс в базе данных, возникает ошибка о дублирующем ключе .
Похоже, что во время процесса возвращенные объекты уже были добавлены в сеанс где-то , и SQLAlchemy пытается добавить их дважды.
Мы пришли к такому выводу, потому что когда мы закомментируем вызов bulk_save_objects()
, сообщение об ошибке больше не появляется. Однако измененные данные фиксируются в базе данных правильно и точно один раз .
Когда мы проверяем базу данных после возникновения этой ошибки, нет записей с Первичным ключом, упомянутым в сообщении об ошибке. Это из-за отката, который происходит по ошибке. Таким образом, дело не в том, что запись уже существует в базе данных, а в том, что сеанс пытается добавить одну и ту же запись дважды.
Это ошибка, которую мы получаем, используя pymssql в качестве драйвера:
sqlalchemy.exc.IntegrityError: (pymssql.IntegrityError) (2627,
b"Violation of PRIMARY KEY constraint 'PK_adminlog_id'.
Cannot insert duplicate key in object 'dbo.adminlog'.
The duplicate key value is (0E5537FF-E45C-40C5-98FC-7B1ACAD8104E).
DB-Lib error message 20018, severity 14:\n
General SQL Server error: Check messages from the SQL Server\n
")
[SQL:
'INSERT INTO adminlog (
alog_id,
alog_ppl_id,
alog_user_ppl_id,
alog_user_name,
alog_datetime,
[alog_ipAddress],
[alog_macAddress],
alog_comment,
alog_type,
alog_act_id,
alog_comp_id,
alog_artc_id)
VALUES (
%(alog_id)s,
%(alog_ppl_id)s,
%(alog_user_ppl_id)s,
%(alog_user_name)s,
%(alog_datetime)s,
%(alog_ipAddress)s,
%(alog_macAddress)s,
%(alog_comment)s,
%(alog_type)s,
%(alog_act_id)s,
%(alog_comp_id)s,
%(alog_artc_id)s)']
[parameters: (
{'alog_act_id': None,
'alog_comment': 'Le service a été ajouté. Cours Coll (119,88)',
'alog_datetime': datetime.datetime(2018, 10, 29, 13, 46, 54, 837178),
'alog_macAddress': b'4A-NO-NY-MO-US',
'alog_type': b'user',
'alog_artc_id': None,
'alog_comp_id': None,
'alog_id': b'0E5537FF-E45C-40C5-98FC-7B1ACAD8104E',
'alog_user_ppl_id': b'99999999-9999-9999-1111-999999999999',
'alog_user_name': 'System',
'alog_ipAddress': b'0.0.0.0',
'alog_ppl_id': b'AE841D1C-5D8D-47F7-B81F-89C5C931BD14'},
{'alog_act_id': None,
'alog_comment': 'Le service a été supprimé.
01/12/2019 Cours Coll (119,88)',
'alog_datetime': datetime.datetime(2018, 10, 29, 13, 46, 55, 71600),
'alog_macAddress': b'4A-NO-NY-MO-US',
'alog_type': b'user',
'alog_artc_id': None,
'alog_comp_id': None,
'alog_id': b'E22176FB-7490-470F-A8BA-A35D5F55A96A',
'alog_user_ppl_id': b'99999999-9999-9999-1111-999999999999',
'alog_user_name': 'System',
'alog_ipAddress': b'0.0.0.0',
'alog_ppl_id': b'AE841D1C-5D8D-47F7-B81F-89C5C931BD14'}
)]
Мы получаем похожую ошибку, используя PyODBC:
sqlalchemy.exc.IntegrityError: (pyodbc.IntegrityError) ('23000',
"[23000] [Microsoft][SQL Server Native Client 11.0][SQL Server]Violation of PRIMARY KEY constraint 'PK_adminlog_id'.
Cannot insert duplicate key in object 'dbo.adminlog'.
The duplicate key value is (F5CABD8F-E000-4677-8F5F-78B4CD3B9560). (2627) (SQLExecDirectW);
[23000] [Microsoft][SQL Server Native Client 11.0][SQL Server]The statement has been terminated. (3621)")
[SQL: 'INSERT INTO adminlog (
alog_id,
alog_ppl_id,
alog_user_ppl_id,
alog_user_name,
alog_datetime,
[alog_ipAddress],
[alog_macAddress],
alog_comment,
alog_type,
alog_act_id,
alog_comp_id,
alog_artc_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)']
[parameters: ((
b'F5CABD8F-E000-4677-8F5F-78B4CD3B9560',
b'0D10D3EF-F37E-45BE-8EED-B5987AE80732',
b'99999999-9999-9999-1111-999999999999',
'System',
datetime.datetime(2018, 10, 29, 13, 51, 30, 555495),
b'0.0.0.0',
b'4A-NO-NY-MO-US',
'Le service a été ajouté. Cours Coll (119,88)',
b'user',
None,
None,
None),
(
b'39395ACA-0AFB-4C5F-90D4-0C6F95D7B8BC',
b'0D10D3EF-F37E-45BE-8EED-B5987AE80732',
b'99999999-9999-9999-1111-999999999999',
'System',
datetime.datetime(2018, 10, 29, 13, 51, 30, 777909),
b'0.0.0.0',
b'4A-NO-NY-MO-US',
'Le service a été supprimé. 01/12/2019 Cours Coll (119,88)',
b'user',
None,
None,
None)
)]
У меня вопрос: существует ли автоматический процесс, который добавляет (изменяет) объекты в сеанс, без использования session.add()
?
Есть ли в SQLAlchemy возможность отключить это поведение и фиксировать сеанс только тогда, когда это явно сделано с использованием session.add(object)
?