#python #sqlalchemy
#python #sqlalchemy
Вопрос:
Я хотел бы знать, как сохранить вычисленное значение чистым способом в базе данных:
Пример (взят из руководства по SQLAlchemy):
Base = declarative_base()
class Interval(Base):
__tablename__ = 'interval'
id = Column(Integer, primary_key=True)
start = Column(Integer)
end = Column(Integer)
@hybrid_property
def length(self):
return self.end - self.start
Так length
вычисляется. Но для упрощения поиска запросов (в другом инструменте базы данных) я хотел бы, чтобы это length
также было сохранено в таблице.
Итак, моим первым грубым тестом было просто добавить: length = Column(Float)
. Очевидно, что это не работает (но я подумал, что все же попробую: p), поскольку метод длины перезаписывает столбец длины.
Прямо сейчас мой единственный способ, о котором я могу думать, это вычислить его в init amp; отложить до super (), вот так:
Base = declarative_base()
class Interval(Base):
__tablename__ = 'interval'
id = Column(Integer, primary_key=True)
start = Column(Integer)
end = Column(Integer)
length = Column(Integer)
def __init__(self, *args, **kwargs):
kwargs['length'] = kwargs['end'] - kwargs['start']
super().__init__(*args, **kwargs)
Однако я чувствую, что должен быть более чистый способ. Есть ли?
Спасибо!
Ответ №1:
Есть несколько способов справиться с этим.
Один из них с before_update
и before_insert
триггерами. Это SQLAlchemy, где изменения в модели обнаруживаются автоматически, а события отправляются до того, как строки записываются на сервер. Затем вы можете использовать это, чтобы предотвратить сохранение, потенциально увидеть, какие столбцы изменились, а затем соответствующим образом обновить length
.
Другой использует generated
или computed column
. Они были введены в Postgres 12, и они также присутствуют в MySQL, Oracle и MS SQL Server, хотя я знаю об этом меньше. Они делают по сути то же самое, но вы просто добавляете эту логику в свое определение таблицы и можете забыть об этом. SQLAlchemy поддерживает их с 1.3.11.
Комментарии:
1. Ваш второй не будет работать, к сожалению, достаточно, поскольку я использую SQLite :). Первый дал мне хорошую подсказку. Сначала я попробовал before_insert , но это было не очень хорошо, так как мне тоже нужен был уровень для чтения. Далее был init_scalar , но это не сработало во время вставки. В итоге я использовал «init». Изменение kwargs в обработчике инициализации сделало то, что мне было нужно. Основная проблема, с которой я сталкиваюсь, заключается в том, что она отделяет инициализацию от самого класса. Так что подумайте, действительно ли это стоит того;-).
Ответ №2:
Вы можете использовать функцию column_property в sqlalchemy вместо hybrid_property.
column_property: Функция column_property() может использоваться для сопоставления выражения SQL способом, аналогичным обычному отображению столбца. С помощью этого метода атрибут загружается вместе со всеми другими атрибутами, отображенными в столбцах, во время загрузки.
Примечание: также вы можете query
использовать column_property.
Base = declarative_base()
class Interval(Base):
__tablename__ = 'interval'
id = Column(Integer, primary_key=True)
start = Column(Integer)
end = Column(Integer)
length = column_property(end - start)
пример запроса:
session.query(Interval).filter(Interval.length == 10)
Комментарии:
1. Большое спасибо за ваш вклад, но
length
как описано вами, не сохраняется в базе данных.