python-3.x 为什么SQL Alchemy异步引擎不持久数据库设置更改

b09cbbtk  于 2023-08-08  发布在  Python
关注(0)|答案(1)|浏览(108)

我试图使用SQLAlchemy和psycopg async驱动程序更新数据库的时区。然而,时区在代码中没有反映为更改,即使在数据库中它看起来像是更改了,代码如下及其输出。
此时,数据库时区设置为欧洲/布加勒斯特,我想将其更改为欧洲/阿姆斯特丹。

  1. import asyncio
  2. from contextlib import asynccontextmanager
  3. from sqlalchemy.orm import sessionmaker
  4. from sqlalchemy.sql import text
  5. from sqlalchemy.ext.asyncio import (
  6. create_async_engine,
  7. AsyncSession,
  8. )
  9. db_uri = "postgresql+psycopg://bigbrother:BigBrother0906!!@10.190.20.72:5432/bigbrother"
  10. db_engine = create_async_engine(db_uri, max_overflow=1, pool_size=2)
  11. @asynccontextmanager
  12. async def db_session_manager(a_db_engine):
  13. session = sessionmaker(a_db_engine, expire_on_commit=False, class_=AsyncSession)()
  14. try:
  15. yield session
  16. await session.commit()
  17. except Exception as ex:
  18. await session.rollback()
  19. raise ex
  20. finally:
  21. await session.close()
  22. async def update_timezone():
  23. async with db_session_manager(db_engine) as session:
  24. await session.execute(
  25. text(
  26. """
  27. ALTER DATABASE "bigbrother"
  28. SET timezone TO 'Europe/Amsterdam'
  29. """
  30. )
  31. )
  32. async def get_timezone():
  33. async with db_session_manager(db_engine) as session:
  34. current_timezone = (
  35. (
  36. await session.execute(
  37. text(
  38. """
  39. SELECT
  40. name, abbrev, utc_offset, is_dst
  41. FROM pg_timezone_names
  42. WHERE name = current_setting('TIMEZONE')
  43. """
  44. )
  45. )
  46. )
  47. .mappings()
  48. .one()
  49. )
  50. print(current_timezone)
  51. print("timezone before")
  52. asyncio.run(get_timezone())
  53. asyncio.run(update_timezone())
  54. print("timezone after update")
  55. asyncio.run(get_timezone())

字符串
这段代码提供了以下输出:
'name'之前的时区:'欧洲/布加勒斯特','v':'EEST','utc_offset':datetime.timedelta(seconds=10800),'is_dst':True}更新后的时区?'name':'欧洲/布加勒斯特','v':'EEST','utc_offset':datetime.timedelta(seconds=10800),'is_dst':真}
这个输出反映了引擎仍然看到旧的时区,但是如果我检查数据库属性,时区似乎改变了,这让我认为这与缓存有关。
如果我再次运行它,从而重新创建引擎,它将提供以下输出:
'name'之前的时区:'欧洲/阿姆斯特丹','v':'CEST','utc_offset':datetime.timedelta(seconds=7200),'is_dst':True}更新后的时区?'name':'欧洲/阿姆斯特丹','v':'CEST','utc_offset':datetime.timedelta(seconds=7200),'is_dst':真}
我想做的是在不重新创建引擎的情况下更改时区,或者从引擎中删除缓存,或者以某种方式刷新引擎,以便它看到我的修改。这意味着,如果当前的数据库时区是Europe/布加勒斯特,而想要的时区是Europe/Amsterdam,我希望我的代码提供以下输出:
'name'之前的时区:'欧洲/布加勒斯特','v':'EEST','utc_offset':datetime.timedelta(seconds=10800),'is_dst':True}更新后的时区?'name':'欧洲/阿姆斯特丹','v':'CEST','utc_offset':datetime.timedelta(seconds=7200),'is_dst':真}
我使用的版本:python==3.8.13 backports.zoneinfo==0.2.1 greenlet== 2.0.2 psycopg==3.1.9 python-dateutil==2.8.2 pytzdata==2020.1六==1.16.0 SQLAlchemy==2.0.19 typing_extensions==4.7.1

yshpjwxd

yshpjwxd1#

这与sqlalchemistry无关,也与asyncio无关,postgresql就是这样工作的。即使在psql会话中执行此操作,也会得到相同的结果。下面是psql示例会话,应该解释:

  1. > select current_setting('timezone'); -- check current session timezone
  2. Europe/Kiev
  3. > select setrole, setconfig from pg_db_role_setting
  4. inner join pg_database on pg_database.oid = pg_db_role_setting.setdatabase
  5. where pg_database.datname = 'testing'; -- select default database settings
  6. setrole | setconfig
  7. ---------+-----------------------------
  8. 0 | {TimeZone=Europe/Kiev}
  9. > alter database testing set timezone to 'Europe/Amsterdam'; -- alter database timzone
  10. > select current_setting('timezone'); -- current session timezone is still the same
  11. Europe/Kiev
  12. > select setrole, setconfig from pg_db_role_setting
  13. inner join pg_database on pg_database.oid = pg_db_role_setting.setdatabase
  14. where pg_database.datname = 'testing'; -- default database settings have changed
  15. setrole | setconfig
  16. ---------+-----------------------------
  17. 0 | {TimeZone=Europe/Amsterdam}

字符串
正如您所看到的,执行alter database <DB> set timezone更改的是默认数据库参数,而不是当前的参数。创建新会话时,它使用这些默认值,因此新会话的timezone值不同。最简单的修复方法是使用set更改当前值,如下所示:

  1. > select current_setting('TIMEZONE');
  2. Europe/Kiev
  3. > set timezone to 'Europe/Amsterdam';
  4. > select current_setting('TIMEZONE');
  5. Europe/Amsterdam


所以你需要更新update_timezone函数并添加set timezone to 'Europe/Amsterdam',脚本的结果如下:

  1. timezone before
  2. {'name': 'Europe/Kiev', 'abbrev': 'EEST', 'utc_offset': datetime.timedelta(seconds=10800), 'is_dst': True}
  3. timezone after update
  4. {'name': 'Europe/Amsterdam', 'abbrev': 'CEST', 'utc_offset': datetime.timedelta(seconds=7200), 'is_dst': True}


然而,这是很可能不是你想要做的,因为这只会在一个连接中改变timezoneset timezone),因为sqlalchemy使用连接池,另一个连接(可能存在),仍然会有另一个timezone的值。
一种选择是只执行set timezone to 'Europe/Amsterdam'作为您执行的第一个SQL表达式,在这种情况下,所有连接都将从一开始就使用这个时区。
另一种选择是在db_uri中提供时区:

  1. db_uri = "postgresql+psycopg://bigbrother:BigBrother0906!!@10.190.20.72:5432/bigbrother?options=-c TIMEZONE=Europe/Amsterdam"


您可以使用此URL检查可用的选项。

展开查看全部

相关问题