Why isn't SQLAlchemy default column value available before object is committed?

后端 未结 3 1080
隐瞒了意图╮
隐瞒了意图╮ 2020-12-05 04:35

Recently I figured out that SQLAlchemy\'s Column default doesn\'t work as I expect it to:

>>> Base = declarative_base()
>>> class TestModel         


        
相关标签:
3条回答
  • 2020-12-05 05:01

    You can use force_instant_defaults listener from sqlalchemy_utils to change this behavior:

    from sqlalchemy_utils import force_instant_defaults
    
    force_instant_defaults()
    
    class TestModel(Base):
        __tablename__ = 'tmodel'
        id = sa.Column(sa.Integer, primary_key=True)
        foo = sa.Column(sa.Integer, default=0)
    
    model = TestModel()
    assert model.foo == 0
    
    0 讨论(0)
  • 2020-12-05 05:07

    one would prefer default over server default for one of four reasons:

    1. you'd like to run a Python function, not a SQL function, for the default (or a SQL expression that needs some per-INSERT Python state also).

    2. the default is part of a primary key column. the ORM can't load a row back without the primary key, so server_default is generally not useful for a PK column when using the ORM.

    3. the SQL expression you want to run isn't supported by the database as a "server default".

    4. You're dealing with a schema you can't/don't want to change.

    In this case, when you'd like "foo" to be "0" in your application independent of database operations, the choices are:

    1. use __init__(). It's python!

    2. use events.

    Here's __init__():

    class TestModel(Base):
       # ...
    
       def __init__(self):
           self.foo = 0
    

    Here's events (specifically the init event):

    from sqlalchemy import event
    
    @event.listens_for(Foo, "init")
    def init(target, args, kwargs):
        target.foo = 0
    
    0 讨论(0)
  • 2020-12-05 05:24

    You can use the init event to fill defaults. This event listener will do it:

    from sqlalchemy import event
    from sqlalchemy.orm import mapper
    from sqlalchemy.inspection import inspect
    
    
    def instant_defaults_listener(target, args, kwargs):
        for key, column in inspect(target.__class__).columns.items():
            if column.default is not None:
                if callable(column.default.arg):
                    setattr(target, key, column.default.arg(target))
                else:
                    setattr(target, key, column.default.arg)
    
    
    event.listen(mapper, 'init', instant_defaults_listener)
    
    0 讨论(0)
提交回复
热议问题