Вопрос по default-value, python, sqlalchemy – Почему недоступно значение столбца SQLAlchemy по умолчанию до фиксации объекта?

26

Недавно я понял, что SQLAlchemyСтолбец по умолчанию не делаетработать так, как я ожидаю:

>>> Base = declarative_base()
>>> class TestModel(Base):
    ...     __tablename__ = 'tmodel'
...     id = sa.Column(sa.Integer, primary_key=True)
...     foo = sa.Column(sa.Integer, default=0)
...    
>>> tmodel_instance = TestModel()
>>> print tmodel_instance.foo
None
>>> session.add(tmodel_instance)
>>> print tmodel_instance.foo
None
>>> session.commit()
>>> print tmodel_instance.foo
0

я хочуtmodel_instance.foo равному0 сразу после создания объекта, но кажется, что значение по умолчанию используется только при выполненииINSERT команда, и это действительно смущает меня. Почему один предпочел быdefault надserver_default? И как мне добиться того, чего я хочу? Я должен указать все аргументы по умолчанию в__init__? Это похоже на дублирование кода: чтобы изменить значение по умолчанию, мне нужно изменить его дважды и сохранить равенство этих значений - есть ли способ избежать этого?

+1 Согласен. Это нене имеет смысла, что акт сохранения объекта изменит егоатрибуты. Mark E. Haase

Ваш Ответ

3   ответа
17

о одной из четырех причин:

вы'Я хотел бы запустить функцию Python, а не функцию SQL, по умолчанию (или выражение SQL, которое также требует некоторого состояния Python для INSERT).

значение по умолчанию является частью столбца первичного ключа. ОРМ можетt загрузить строку назад без первичного ключа, поэтому server_default, как правило, бесполезен для столбца PK при использовании ORM.

выражение SQL, которое вы хотите запустить isn 'поддерживается базой данных как "сервер по умолчанию ".

Вы'Вы имеете дело со схемой, которую можетет / Дон»не хочу меняться

В этом случае, когда вымне нравитсяFoo» быть "0" в вашем приложении, независимо от операций с базой данных, возможны следующие варианты:

использование__init__(), Это'с питоном!

использовать события.

Вот:__init__()

class TestModel(Base):
   # ...

   def __init__(self):
       self.foo = 0

Вот'события (в частности,событие init):

from sqlalchemy import event

@event.listens_for(Foo, "init")
def init(target, args, kwargs):
    target.foo = 0
Но почему модели sqlAlchemy были разработаны таким образом: «Нет» вместо значения по умолчанию сразу после создания? Это определенно смущает людей, которые привыкли к другим библиотекам ORM d9k
"дефолт" Чаще всего это выражение SQL или полное значение на стороне сервера. Он не может быть известен до тех пор, пока не произойдет INSERT, или гораздо менее в идеале, если он был неуклюже предварительно выполнен при создании объекта (что само по себе ухудшает значение как истинное)ВСТАВИТЬ ДЭВАУТ "). поэтому сделать только одно подмножество значений по умолчанию для вставки на уровне ядра будет совершенно иначе, было бы несовместимо. zzzeek
4

Вы можете использоватьinit событие для заполнения значений по умолчанию. Этот слушатель события сделает это:

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)
3

Ты можешь использоватьforce_instant_defaults слушатель изsqlalchemy_utils изменить это поведение:

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

Похожие вопросы