Подтвердить что ты не робот

Как применять значения столбца по умолчанию перед фиксацией в sqlalchemy

У меня есть модель с декларативной базой:

class User(Base):
    id = Column(Integer, primary_key=True)
    money = Column(Integer, default=100)

а затем я запустил

>>> u = User()
>>> u.money
None

Как я могу заполнить значения по умолчанию с помощью sqlalchemy, не записывая ничего в базу данных?

4b9b3361

Ответ 1

Значение по умолчанию для столбцов применяется только к операторам INSERT и UPDATE и поэтому не применяется до тех пор, пока вы не выполните .flush() сеанс.

Чтобы увидеть то же самое значение для новых экземпляров перед флешем, вам необходимо применить значение по умолчанию при создании нового экземпляра; в методе __init__ объекта User:

class User(Base):
    __tablename__ = 'users'

    def __init__(self, **kwargs):
        if 'money' not in kwargs:
             kwargs['money'] = self.__table__.c.money.default.arg
        super(User, self).__init__(**kwargs)

    id = Column(Integer, primary_key=True)
    money = Column(Integer, default=100)

Если атрибут money не установлен, мы добавляем его непосредственно по умолчанию, заданному для столбца.

Обратите внимание, что по умолчанию используются выражения SQL, а не значения Python, поэтому вам, возможно, придется сначала сопоставить их с объектами Python. Например, логическое поле будет иметь строковое значение по умолчанию 'false' или 'true', а не False или True булевский объект Python.

Ответ 2

Я обнаружил механизм для заполнения некоторых значений по умолчанию автоматически при создании экземпляра. Я перехожу из внутреннего ORM, который задает значения по умолчанию при создании экземпляра, поэтому мне нужно было сохранить это поведение, и я не хотел касаться каждого определения модели.

BaseModel = declarative_base()

class Base(BaseModel):
    __abstract__ = True

    def __init__(self, **kwargs):
        for attr in self.__mapper__.column_attrs:
            if attr.key in kwargs:
                continue

            # TODO: Support more than one value in columns?
            assert len(attr.columns) == 1
            col = attr.columns[0]

            if col.default and not callable(col.default.arg):
                kwargs[attr.key] = col.default.arg

        super(Base, self).__init__(**kwargs)

Основное ограничение заключается в том, что поддерживаются только неконкретные значения по умолчанию. Вызываемые по умолчанию значения и те, которые используют запрос базы данных, требуют контекста выполнения, который недоступен во время создания экземпляра. Кроме того, я не обнаружил случай, когда len(ColumnProperty.columns) != 1, но это тоже не поддерживается.

Ответ 3

Так как мне нужны значения по умолчанию, не обязательно в конструкторе, но иногда из других мест, я придумал функцию, которую я добавил в базовый класс Object:

Base = declarative_base()
class ItemBase(Base):
    __abstract__ = True
    def _ensure_defaults(self):
        for column in self.__table__.c:
            if getattr(self, column.name) is None and column.default is not None and column.default.is_scalar:
                setattr(self, column.name, column.default.arg)

Что явно не работает для вызывающих. Я не мог понять, как это назвать, и, может быть, это к лучшему (подумайте, как взломать мысли уже).

С этим я могу сделать что-то вроде:

class User(ItemBase):
    # ... inheritance, columns and stuff
    def __init__(self):
        self._ensure_defaults()

Я также могу вызвать метод не в __init__, но также и из других методов, в случае, если мне нужно полагаться только на значения в особых обстоятельствах (которые я в конечном итоге удалял позже, так как это было слишком запутанным). В некоторых случаях это может быть более гибким, чем решение @JamesEmerton.