根据 Aiohttp 官方文档教程学习,在使用 Aiomysql 配合 SQLAlchemy 时,我遇到了两个十分坑爹的问题,白白折腾了好几个小时。而在各大搜索引擎里也没有解答,于是写下这篇文章希望对以后踩坑的人有些帮助。

id 为自增主键,insert 报错

首先在表的定义为如下:

users = Table(
    'users', meta,
    
    Column('id', Integer, primary_key=True),
    Column('name', String(32), nullable=False, unique=True, server_default=text("'0'")),
    Column('password', String(80), nullable=False, server_default=text("'0'")),
)

aiohttp 官方教程中 init_db.py 的代码配合以上表:

from settings import config
from blog.models import users

DSN = "mysql+pymysql://{user}:{password}@{host}:{port}/{database}"

def sample_data(engine):
    conn = engine.connect()
    conn.execute(users.insert(), [
        {'name': 'veoco',
         'password':'123456',
    ])
    conn.close()


if __name__ == '__main__':
    db_url = DSN.format(**config['mysql'])
    engine = create_engine(db_url)

    sample_data(engine)

默认情况下 id 即为自增,在这种情况下插入语句应该可以省略 id。事实也是如此,按照 Aiohttp 教程中的 init_db.py 流程插入毫无问题,但按照教程从 app 取出 engine 插入就会报错:sqlalchemy.exc.InvalidRequestError: A value is required for bind parameter 'id'

报错说明必须要有 id 的参数,但是 id 原本就是自增不应该手动插入,解决的方法就是添加 id 参数,只不过参数为 None,我的代码如下:

async with request.app['db'].acquire() as conn:
    await conn.execute(users.insert(), [
        {'id': None,
         'name': username,
         'password': password, 
        ])

由此,主键自增情况下插入报错的问题解决了,这时又发现了一个新问题。

aiomysql 执行后没有生效

除去前面增加的 id 这列,不知道你有没有发现两处代码的不一样。答案就是 engine,init_db.py 中为 engine.connect() 而在 aiohttp 教程中为 engine.accquire(),这两者实现并不一样,后者在 select 时无需额外动作,而在 insert 时需要在之后 commit,添加后的代码如下:

async with request.app['db'].acquire() as conn:
    await conn.execute(users.insert(), [
        {'id': None,
         'name': username,
         'password': password, 
        ])
    await conn.execute('commit')

由此终于可以在 Aiohttp 下愉快的使用 Aiomysql 和 SQLAlchemy 了。