默认值在 SQLAlchemy + PostgreSQL + aiopg + psycopg2 中不起作用

Default value doesn't work in SQLAlchemy + PostgreSQL + aiopg + psycopg2

我在 SQLAlchemy 中发现了意外行为。我正在使用以下版本:

这是示例的 table 定义:

import asyncio
from aiopg.sa import create_engine
from sqlalchemy import (
  MetaData,
  Column,
  Integer,
  Table,
  String,
)
metadata = MetaData()

users = Table('users', metadata,
  Column('id_user', Integer, primary_key=True, nullable=False),
  Column('name', String(20), unique=True),
  Column('age', Integer, nullable=False, default=0),
)

现在,如果我尝试对 table 执行简单插入,只需填充 id_username,列 age 应该自动生成正确的?让我们看看...

@asyncio.coroutine
def go():
  engine = yield from create_engine('postgresql://USER@localhost/DB')
  data = {'id_user':1, 'name':'Jimmy' }
  stmt = users.insert(values=data, inline=False)
  with (yield from engine) as conn:
    result = yield from conn.execute(stmt)


loop = asyncio.get_event_loop()
loop.run_until_complete(go())

这是带有相应错误的结果语句:

INSERT INTO users (id_user, name, age) VALUES (1, 'Jimmy', null);

psycopg2.IntegrityError: null value in column "age" violates not-null constraint

我没有提供 age 列,那么 age = null 值来自哪里?我期待这样的事情:

INSERT INTO users (id_user, name) VALUES (1, 'Jimmy');

或者如果 default 标志实际有效应该是:

INSERT INTO users (id_user, name, Age) VALUES (1, 'Jimmy', 0);

你能解释一下吗?

我认为您需要在插入内容中使用 inline=True。这将关闭 'pre-execution'。 文档对这个 'pre-execution' 的确切含义有点含糊不清,但他们提到了默认参数:

    :param inline:
      if True, SQL defaults present on :class:`.Column` objects via
      the ``default`` keyword will be compiled 'inline' into the statement
      and not pre-executed.  This means that their values will not
      be available in the dictionary returned from
      :meth:`.ResultProxy.last_updated_params`.

这段文档字符串来自 Update class,但它们与 Insert 有共同的行为。

此外,这是他们测试它的唯一方法: https://github.com/zzzeek/sqlalchemy/blob/rel_0_9/test/sql/test_insert.py#L385

已确认此问题存在 aiopg 错误。似乎目前它忽略了关于数据操作的 default 参数。

我已经使用 server_default 解决了这个问题:

users = Table('users', metadata,
          Column('id_user', Integer, primary_key=True, nullable=False),
          Column('name', String(20), unique=True),
          Column('age', Integer, nullable=False, server_default='0'))