Estoy tratando de averiguar cómo mapear contra una simple propiedad de solo lectura y hacer que esa propiedad se active cuando guardo en la base de datos.SQLAlchemy: cómo hacer un mapa con una propiedad de solo lectura (o calculada)
Un ejemplo artificial debería hacer esto más claro. En primer lugar, una tabla sencilla:
meta = MetaData()
foo_table = Table('foo', meta,
Column('id', String(3), primary_key=True),
Column('description', String(64), nullable=False),
Column('calculated_value', Integer, nullable=False),
)
Lo que quiero hacer es configurar una clase con una propiedad de sólo lectura que va a insertar en la columna de la calculated_value para mí cuando llamo session.commit() ...
import datetime
def Foo(object):
def __init__(self, id, description):
self.id = id
self.description = description
@property
def calculated_value(self):
self._calculated_value = datetime.datetime.now().second + 10
return self._calculated_value
de acuerdo con los documentos SQLAlchemy, yo creo supone que tengo que asignar este modo:
mapper(Foo, foo_table, properties = {
'calculated_value' : synonym('_calculated_value', map_column=True)
})
El problema con esto es que _calculated_ el valor es Ninguno hasta que acceda a la propiedad calculate_value. Parece que SQLAlchemy no está llamando a la propiedad al insertarla en la base de datos, por lo que recibiré un valor None. ¿Cuál es la forma correcta de asignar esto para que el resultado de la propiedad "calculate_value" se inserte en la columna "calculate_value" de la tabla foo?
OK - Estoy editando esta publicación en caso de que alguien más tenga la misma pregunta. Lo que terminé haciendo fue usar una MapperExtension. Déjame darte un mejor ejemplo junto con el uso de la extensión:
class UpdatePropertiesExtension(MapperExtension):
def __init__(self, properties):
self.properties = properties
def _update_properties(self, instance):
# We simply need to access our read only property one time before it gets
# inserted into the database.
for property in self.properties:
getattr(instance, property)
def before_insert(self, mapper, connection, instance):
self._update_properties(instance)
def before_update(self, mapper, connection, instance):
self._update_properties(instance)
Y así es como lo usas. Digamos que tienes una clase con varias propiedades de solo lectura que deben activarse antes de insertarla en la base de datos. Supongo que aquí para cada una de estas propiedades de solo lectura, tiene una columna correspondiente en la base de datos que desea rellenar con el valor de la propiedad. Todavía se va a establecer un sinónimo para cada propiedad, pero se utiliza la extensión asignador anterior cuando asigna el objeto:
class Foo(object):
def __init__(self, id, description):
self.id = id
self.description = description
self.items = []
self.some_other_items = []
@property
def item_sum(self):
self._item_sum = 0
for item in self.items:
self._item_sum += item.some_value
return self._item_sum
@property
def some_other_property(self):
self._some_other_property = 0
.... code to generate _some_other_property on the fly....
return self._some_other_property
mapper(Foo, metadata,
extension = UpdatePropertiesExtension(['item_sum', 'some_other_property']),
properties = {
'item_sum' : synonym('_item_sum', map_column=True),
'some_other_property' : synonym('_some_other_property', map_column = True)
})
Interesante.Aunque resolví esto de una manera ligeramente diferente, lo marqué como la respuesta porque debería funcionar. ¡Cuanto más aprendo sobre SQLAlchemy, más me gusta! –