3

我正在使用带有asyncio++ (ORM-ish)+ asyncpg(路由、网络响应)的 Postgres、Python3.7。ginoaiohttp

users我在我的数据库中创建了一个小型 postgres 表testdb并插入了一行:

testdb=# select * from users;
 id | nickname
----+----------
  1 | fantix

我正在尝试设置我的数据库,以便在请求进入时可以在路由中使用 ORM。

import time
import asyncio
import gino

DATABASE_URL = os.environ.get('DATABASE_URL')

db = gino.Gino()

class User(db.Model):
  __tablename__ = 'users'
  id = db.Column(db.Integer(), primary_key=True)
  nickname = db.Column(db.Unicode(), default='noname')

kwargs = dict(
  min_size=10,
  max_size=100,
  max_queries=1000,
  max_inactive_connection_lifetime=60 * 5,
  echo=True
)

async def test_engine_implicit():
  await db.set_bind(DATABASE_URL, **kwargs)
  return await User.query.gino.all()      # this works

async def test_engine_explicit():
  engine = await gino.create_engine(DATABASE_URL, **kwargs)
  db.bind = engine
  async with engine.acquire() as conn:
    return await conn.all(User.select())  # this doesn't work!

users = asyncio.get_event_loop().run_until_complete(test_engine_implicit())
print(f'implicit query: {users}')

users = asyncio.get_event_loop().run_until_complete(test_engine_explicit())
print(f'explicit query: {users}')

输出是:

web_1    | INFO gino.engine._SAEngine SELECT users.id, users.nickname FROM users
web_1    | INFO gino.engine._SAEngine ()
web_1    | implicit query: [<db.User object at 0x7fc57be42410>]
web_1    | INFO gino.engine._SAEngine SELECT
web_1    | INFO gino.engine._SAEngine ()
web_1    | explicit query: [()]

这很奇怪。“显式”代码本质上是SELECT针对数据库运行的,这是无用的。

我在文档中找不到一种方法来 1) 使用 ORM,以及 2) 明确地检查池中的连接。

我的问题:

  1. 是否await User.query.gino.all()检查池中的连接?它是如何发布的?
  2. 我将如何在事务中包装查询?我很不安,因为我无法明确控制何时/何地从池中获取连接,以及如何释放它。

我基本上喜欢test_engine_explicit()与 Gino 一起工作的风格的明确性,但也许我只是不理解 Gino ORM 的工作原理。

4

1 回答 1

3

我以前从未使用过 GINO,但在快速查看代码后:

  1. GINO 连接只是按原样执行提供的子句。因此,如果您提供 bare User.select(),那么它不会增加任何内容。
  2. 如果您想实现与 using 相同的功能User.query.gino.all(),但要自己维护连接,那么您可以按照文档使用 useUser.query而不是 plain User.select()
async with engine.acquire() as conn:
    return await conn.all(User.query) 

刚刚测试过,对我来说效果很好。

关于连接池,我不确定我的问题是否正确,但Engine.acquire 默认情况下会创建一个可重用的连接,然后将其添加到池中,这实际上是一个堆栈:

   :param reusable: Mark this connection as reusable or otherwise. This
      has no effect if it is a reusing connection. All reusable connections
      are placed in a stack, any reusing acquire operation will always
      reuse the top (latest) reusable connection. One reusable connection
      may be reused by several reusing connections - they all share one
      same underlying connection. Acquiring a connection with
      ``reusable=False`` and ``reusing=False`` makes it a cleanly isolated
      connection which is only referenced once here.

GINO中还有一个手动事务控制,例如,您可以创建一个不可重用、不可重用的连接并手动控制事务流:

async with engine.acquire(reuse=False, reusable=False) as conn: 
     tx = await conn.transaction() 
     try: 
          await conn.status("INSERT INTO users(nickname) VALUES('e')") 
          await tx.commit() 
     except Exception: 
          await tx.rollback() 
          raise

至于连接释放,我找不到任何证据表明GINO自己释放连接。我猜这个池是由 SQLAlchemy 核心维护的。

我肯定没有直接回答你的问题,但希望它能以某种方式帮助你。

于 2019-10-30T14:40:17.807 回答