Кастомизироваать поле orm-модели
Я использую sqlalchemy. У меня есть orm-модель:
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.schema import Column
from sqlalchemy.sql.sqltypes import BigInteger
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
ModelWorker = declarative_base(name='ModelGenerator')
Engine = create_engine('sqlite://', echo=False)
Session = sessionmaker(bind=Engine)
class MyField(Column):
pass
class MyModel(ModelWorker):
__tablename__ = 'my_model'
id = Column(BigInteger, primary_key=True)
value = MyField(BigInteger)
ModelWorker.metadata.create_all(Engine)
model = MyModel(id=1, value=100)
session = Session()
session.add(model)
session.commit()
Я хочу кастомизировать поле value, чтобы при присваиванию ему значения оно, допустим, выполняло добавляло к нему 10 (model = MyModel(id=1, value=100);model.value == 110). Мне надо как-то изменить класс MyField или MyModel для этого, и как это сделать?
Ответы (1 шт):
Ответ несколько сложнее, чем я думал. С одной стороны, он прост:
class MyModel(ModelWorker):
__tablename__ = 'my_model'
id = Column(BigInteger, primary_key=True)
value = MyField(BigInteger)
def __init__(self, *args, **kwargs):
if 'value' in kwargs:
# Do code
kwargs[name] += 10
super().__init__(*args, **kwargs)
И для разового использования вполне годится. Для использования же в разных моделях каждый раз писать название полей очень плохо и некрасиво. Поэтому можно сделать так:
class BaseModel(ModelWorker):
__abstract__ = True
def __init__(self, *args, **kwargs):
for (name, value) in kwargs.items():
field_class = getattr(self, name, None)
if isinstance(field_class, MyField):
# Do code
kwargs[name] += 10
super().__init__(*args, **kwargs)
class MyModel(BaseModel):
__tablename__ = 'my_model'
id = Column(BigInteger, primary_key=True)
value = MyField(BigInteger)
И тут есть одна неожиданная особенность. Проблема возникает, когда мы вызываем isinstance(field_class, MyField), и заключается в том, что при инициализации класса все поля переводятся Column -> InstrumentedAttribute:
>>> MyModel.value
# <sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x7f01518b2ea0>
Для получения оригинального списка столбцов нужно обращаться к MyModel.__table__.columns:
class BaseModel(ModelWorker):
__abstract__ = True
def __init__(self, *args, **kwargs):
for (name, value) in kwargs.items():
field_class = self.__table__.columns.get(name, None)
if isinstance(field_class, MyField):
# Do code
kwargs[name] += 10
super().__init__(*args, **kwargs)
class MyModel(BaseModel):
__tablename__ = 'my_model'
id = Column(BigInteger, primary_key=True)
value = MyField(BigInteger)
model = MyModel(id=1, value=1)
my.value # 11