[Sqlalchemy-tickets] Issue #4278: NULL does not override default value if new instance is created w
Brought to you by:
zzzeek
From: jan.budinsky <iss...@bi...> - 2018-06-14 14:50:30
|
New issue 4278: NULL does not override default value if new instance is created when session.merge(row) https://bitbucket.org/zzzeek/sqlalchemy/issues/4278/null-does-not-override-default-value-if jan.budinsky: Default column value is not overridden by `NULL` in `Declarative API` as it should be. If a column has a non-null default value that is overridden by another non-null value, everything works as expected - the specified value is always inserted as the value of the column instead of the default one. This however does not seem to be the case when overriding non-null default value with `NULL`. If a matching row (via primary key) has been already created (`merge` does `UPDATE`), the column will have `NULL` filled as its value, as expected. But if a matching row hasn't been created yet (`merge` does `INSERT INTO`), the column will have default value instead of expected `NULL`. The issue is the same using both `postgresql` and `in-memory sqlite`. Please see the script below reproducing the issue, thank you. ``` #!python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Column from sqlalchemy.dialects.postgresql import BIGINT, VARCHAR CONN_STR = "sqlite:///:memory:" Base = declarative_base() class Table(Base): __tablename__ = "table" id = Column(BIGINT, primary_key=True) field = Column(VARCHAR, default="placeholder") def merge(session, row): session.begin() try: session.merge(row) session.commit() except: session.rollback() raise def select(session, table, **criteria): return session.query(table).filter_by(**criteria).all() def delete(session, table, **criteria): return session.query(table).filter_by(**criteria).delete() def main(): engine = create_engine(CONN_STR, echo=False) Base.metadata.create_all(engine) # create table session = scoped_session(sessionmaker(bind=engine, autocommit=True))() data_null = { "id": 1, "field": None } data_value = { "id": 2, "field": "value" } # cleanup delete(session=session, table=Table, id=data_null["id"]) delete(session=session, table=Table, id=data_value["id"]) merge(session=session, row=Table(**data_null)) print(select(session=session, table=Table, id=data_null["id"])[0].field) # "placeholder" ??? merge(session=session, row=Table(**data_null)) print(select(session=session, table=Table, id=data_null["id"])[0].field) # None merge(session=session, row=Table(**data_value)) print(select(session=session, table=Table, id=data_value["id"])[0].field) # "value" merge(session=session, row=Table(**data_value)) print(select(session=session, table=Table, id=data_value["id"])[0].field) # "value" if __name__ == "__main__": main() ``` Environment: ``` python version - 3.6.3 SQLAlchemy version - 1.2.8 ``` |