如何告诉SQLAlchemy在插入时忽略特定的列(比如空列)?

11

我有一个遗留数据库,它使用多种存储过程为多个列创建默认值。试图追踪名称并添加查询到我的代码会是相当繁琐的过程,更不用说维护上的麻烦了。

我希望能够告诉SQLAlchemy忽略我并不关心的列。但很遗憾,它并没有提供这样的功能。相反,它提供了null值,这违反了数据库的约束条件。

以下是我想表达的示例:

import sqlalchemy as sa
import logging
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

l = logging.getLogger('sqlalchemy.engine')
l.setLevel(logging.INFO)
l.addHandler(logging.StreamHandler())




engine = sa.create_engine('postgresql+psycopg2://user@host:port/dbname')

Session = sessionmaker(bind=engine)
session = Session()
temp_metadata = sa.MetaData(schema='pg_temp')
TempBase = declarative_base(metadata=temp_metadata)

with session.begin(subtransactions=True):
    session.execute('''
        CREATE TABLE pg_temp.whatevs (
            id      serial
          , fnord   text not null default 'fnord'
          , value   text not null
        );

        INSERT INTO pg_temp.whatevs (value) VALUES ('something cool');
    ''')

    class Whatever(TempBase):
        __tablename__ = 'whatevs'

        id = sa.Column('id', sa.Integer, primary_key=True, autoincrement=True)
        fnord = sa.Column('fnord', sa.String)
        value = sa.Column('value', sa.String)

    w = Whatever(value='something cool')
    session.add(w)

这个出错了,原因如下:

INSERT INTO pg_temp.whatevs (fnord, value) VALUES (%(fnord)s, %(value)s) RETURNING pg_temp.whatevs.id
{'fnord': None, 'value': 'something cool'}
ROLLBACK
Traceback (most recent call last):
  File "/home/wayne/.virtualenvs/myenv/lib64/python3.5/site-packages/sqlalchemy/engine/base.py", line 1139, in _execute_context
    context)
  File "/home/wayne/.virtualenvs/myenv/lib64/python3.5/site-packages/sqlalchemy/engine/default.py", line 450, in do_execute
    cursor.execute(statement, parameters)
psycopg2.IntegrityError: null value in column "fnord" violates not-null constraint
DETAIL:  Failing row contains (2, null, something cool).

我所期望的是它跳过了"fnord"列,因为它没有被设置。

即使我执行:

w = Whatever()
w.value = 'this breaks too'

或者添加:

def __init__(self, value):
    self.value = value

对于Whatever类,仍然不行。

我该如何告诉SQLAlchemy,“这些其他列很好,我知道我没有提供值 - 数据库会为我处理。没关系,只是不要担心这些列”? 我所知道的唯一方法就是在类定义中搞鬼并撒谎,说这些列不存在......但是我确实希望它们在查询中出现。

1个回答

8

fnord 添加一个 服务器端默认,使用 server_default

class Whatever(TempBase):
    __tablename__ = 'whatevs'

    id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
    fnord = sa.Column(sa.String, nullable=False, server_default='fnord')
    value = sa.Column(sa.String, nullable=False)

SQLAlchemy会很愉快地让默认设置在服务器端生效,只需要告诉它即可。如果您有没有在DDL中设置默认值的列,但是通过触发器、存储过程或类似方式进行了设置,请查看FetchedValue
SQLite测试:
In [8]: engine.execute("""CREATE TABLE whatevs (
   ...:  id INTEGER NOT NULL,
   ...:  fnord VARCHAR DEFAULT 'fnord' NOT NULL,
   ...:  value VARCHAR NOT NULL,
   ...:  PRIMARY KEY (id)
   ...: )""")

In [12]: class Whatever(Base):
    ...:     __tablename__ = 'whatevs'
    ...:     id = Column(Integer, primary_key=True, autoincrement=True)
    ...:     fnord = Column(String, nullable=False, server_default="fnord")
    ...:     value = Column(String, nullable=False)
    ...:     

In [13]: session.add(Whatever(value='asdf'))

In [14]: session.commit()
2016-08-31 23:46:09,826 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
INFO:sqlalchemy.engine.base.Engine:BEGIN (implicit)
2016-08-31 23:46:09,827 INFO sqlalchemy.engine.base.Engine INSERT INTO whatevs (value) VALUES (?)
INFO:sqlalchemy.engine.base.Engine:INSERT INTO whatevs (value) VALUES (?)
2016-08-31 23:46:09,827 INFO sqlalchemy.engine.base.Engine ('asdf',)
INFO:sqlalchemy.engine.base.Engine:('asdf',)
2016-08-31 23:46:09,828 INFO sqlalchemy.engine.base.Engine COMMIT
INFO:sqlalchemy.engine.base.Engine:COMMIT

看起来 FetchedValue 正是我所需要的,谢谢! - Wayne Werner
1
我以为自己疯了,感谢你展示了需要“server_default”的情况。但这真的很令人沮丧,因为现在我要在两个地方指定服务器默认值,而这应该是一种反模式。 - kevlarr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接