#python #database #orm #sqlalchemy
#python #База данных #orm #sqlalchemy
Вопрос:
У меня есть таблица SQLAlchemy, определенная следующим образом:
user = Table('user', MetaData(),
Column('id', Integer),
Column('first_name', String),
Column('last_name', String))
Мне часто нужно ссылаться на полное имя пользователя во время запроса, например:
sql = (select([
user.c.id,
(user.c.first_name user.c.last_name).label('full_name')
]).where(user.c.id == 123))
Поскольку full_name используется во многих местах, поэтому такой код имеет много копий.
Интересно, есть ли в SQLAlchemy способ, которым я могу создать вычисляемый столбец, чтобы я мог удобно использовать его так же, как и другой обычный столбец, SQLAlchemy автоматически преобразует его в (user.c.first_name user.c.last_name).label('full_name')
всякий раз, когда я ссылаюсь user.c.full_name
sql = (select([
user.c.id,
user.c.full_name
]).where(user.c.id == 123))
Я искал и нашел там какое-то решение в SQLAlchemy ORM, используя column_property или hybrid_property. Разница в моем случае заключается в том, что я могу использовать только ядро SQLAlchemy.
Ответ №1:
Невозможно создать вычисляемые столбцы в ядре sqlalchemy. Однако вам не нужно этого делать, все, что требуется, это сохранить ваше выражение в переменной, а затем использовать это в ваших операторах выбора. Если у вас их много для хранения, вы можете использовать для них пространство имен, сохранив их все в коллекции. В приведенном ниже примере я использовал для этой цели объект свойств SQLAlchemy, поэтому он будет вести себя аналогично коллекции columns .
class Table(sqlalchemy.Table):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.d = sqlalchemy.util._collections.Properties({})
user = Table('user', MetaData(),
Column('id', Integer),
Column('first_name', String),
Column('last_name', String))
user.d.full_name = (user.c.first_name user.c.last_name).label('full_name')
user.d.backwards_name = (user.c.last_name user.c.first_name).label('backwards_name')
sql = (select([
user.c.id,
user.d.full_name
]).where(user.c.id == 123))
Комментарии:
1. Может возникнуть конфликт имен, если я создам производный столбец непосредственно в экземпляре таблицы,
full_name
это просто пример, на самом деле это могут быть любые слова. Мой проект в значительной степени зависит отtable.c
выполнения большого количества проверок, связанных с столбцами, поэтому следуйте вашему предложению, я могу переместитьfull_name
экземпляр в table.cColumnCollection
. Но я проверил исходный код, ColumnCollection принимает только экземпляр столбца. Если есть способ, которым я могу обойти и внедрить его в ColumnCollection, это приемлемо для меня.2. Я отредактировал свой ответ на namespace выражения sql, сохранив их в новой коллекции. Было бы просто использовать monkeypatch для коллекции столбцов, чтобы принять их, но это, вероятно, нарушило бы все остальные фрагменты кода, которые ожидали найти там только столбцы. Я бы предположил, что имеет смысл хранить эти коллекции отдельно, поскольку они содержат разные вещи. Если вы действительно хотите получить к ним доступ вместе, вы можете перейти
self.d = Properties({})
наself.d = Properties(ChainMap({}, self.c._data))
. иuser.d
вместо этого получите доступ ко всему.3. Спасибо @EAW, хотя ваша идея не решает всех моих проблем, для меня это все еще очень хорошее направление. Вся исходная
Table
функциональность не будет нарушена при использовании свойства extendd