【问题标题】:How do I tell sqlalchemy to ignore certain (say, null) columns on INSERT如何告诉 sqlalchemy 忽略 INSERT 上的某些(比如 null)列
【发布时间】:2017-01-08 10:28:44
【问题描述】:

我有一个遗留数据库,它使用各种存储过程为多个列创建默认值。尝试追踪名称并向我的代码添加查询或多或少会让人望而却步,更不用说维护噩梦了。

我希望喜欢的是能够告诉 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 “看,这些其他列很好,我知道我没有提供值 - 数据库会为我处理这些。没关系,不用担心这些列”?

我知道的唯一方法是使用类定义并撒谎,说这些列不存在......但我确实实际上希望它们进入查询。

【问题讨论】:

    标签: python postgresql sqlalchemy


    【解决方案1】:

    为fnord 添加server side default 和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 中没有设置默认值,但有 through triggers、存储过程等,请查看 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 正是我所需要的,谢谢!
    • 以为我疯了,所以感谢您展示您需要server_default。不过,这太令人沮丧了,因为现在我要在 2 个地方指定服务器默认值,这应该是一种反模式。
    猜你喜欢
    • 2012-03-25
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-26
    • 2014-05-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多