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

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

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

import asyncio
from contextlib import asynccontextmanager
from sqlalchemy.orm import sessionmaker
from sqlalchemy.sql import text
from sqlalchemy.ext.asyncio import (
    create_async_engine,
    AsyncSession,
)

db_uri = "postgresql+psycopg://bigbrother:BigBrother0906!!@10.190.20.72:5432/bigbrother"
db_engine = create_async_engine(db_uri, max_overflow=1, pool_size=2)

@asynccontextmanager
async def db_session_manager(a_db_engine):
    session = sessionmaker(a_db_engine, expire_on_commit=False, class_=AsyncSession)()
    try:
        yield session
        await session.commit()
    except Exception as ex:
        await session.rollback()
        raise ex
    finally:
        await session.close()

async def update_timezone():
    async with db_session_manager(db_engine) as session:
        await session.execute(
            text(
                """
            ALTER DATABASE "bigbrother"
            SET timezone TO 'Europe/Amsterdam'
        """
            )
        )

async def get_timezone():
    async with db_session_manager(db_engine) as session:
        current_timezone = (
            (
                await session.execute(
                    text(
                        """
                SELECT
                    name, abbrev, utc_offset, is_dst
                FROM pg_timezone_names
                WHERE name = current_setting('TIMEZONE')
            """
                    )
                )
            )
            .mappings()
            .one()
        )
        print(current_timezone)

print("timezone before")
asyncio.run(get_timezone())
asyncio.run(update_timezone())
print("timezone after update")
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示例会话,应该解释:

> select current_setting('timezone'); -- check current session timezone
 Europe/Kiev

> select setrole, setconfig from pg_db_role_setting 
  inner join pg_database on pg_database.oid = pg_db_role_setting.setdatabase
  where pg_database.datname = 'testing'; -- select default database settings

setrole |          setconfig          
---------+-----------------------------
       0 | {TimeZone=Europe/Kiev}

> alter database testing set timezone to 'Europe/Amsterdam'; -- alter database timzone

> select current_setting('timezone'); -- current session timezone is still the same
 Europe/Kiev

> select setrole, setconfig from pg_db_role_setting 
  inner join pg_database on pg_database.oid = pg_db_role_setting.setdatabase
  where pg_database.datname = 'testing'; -- default database settings have changed

 setrole |          setconfig          
---------+-----------------------------
       0 | {TimeZone=Europe/Amsterdam}

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

> select current_setting('TIMEZONE');
Europe/Kiev
> set timezone to 'Europe/Amsterdam';
> select current_setting('TIMEZONE');
 Europe/Amsterdam


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

timezone before
{'name': 'Europe/Kiev', 'abbrev': 'EEST', 'utc_offset': datetime.timedelta(seconds=10800), 'is_dst': True}
timezone after update
{'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中提供时区:

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


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

相关问题