2010-06-11 21 views
5

Basit bir salt okunur özelliğe karşı eşleme yapmanın nasıl bir yol olduğunu bulmaya çalışıyorum ve veritabanına kaydederken bu özelliğin yangına neden olduğunu düşünüyorum.SQLAlchemy - salt okunur (veya hesaplanmış) özelliğe karşı eşleme nasıl yapılır

Bunun için bir örnek, bunu daha açık hale getirmelidir. İlk olarak, basit tablo:

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), 
    ) 

Ya ben session.commit (çağırdığınızda benim için calculated_value sütuna ekler salt okunur bir özelliğe sahip bir sınıf kurulur yapmak istiyorum) ...

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 

sqlalchemy docs göre ben şöyle bu harita gerekiyor düşünüyorum :

mapper(Foo, foo_table, properties = { 
    'calculated_value' : synonym('_calculated_value', map_column=True) 
    }) 

bu sorun o _calculated_ olduğunu value_value özelliğine erişene kadar değer Yoktur. SQLAlchemy, veritabanına ekleme üzerine özelliği çağırmıyor gibi görünüyor, bu yüzden bunun yerine bir None değeri alıyorum. Bunu "map_value" özelliğinin sonucu foo tablosunun "calculation_değer_değer" sütununa eklenecek şekilde eşlemek için doğru yol nedir?

Tamam - Başka birinin aynı soruna sahip olması durumunda bu gönderiyi düzenliyorum. Yaptığım şey bir MapperExtension kullanmaktı. Uzantının kullanımıyla birlikte size daha iyi bir örnek vereyim:

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) 

Bu şekilde kullanabilirsiniz. Veritabanına yerleştirilmeden önce yanması gereken birkaç salt okunur özelliği olan bir sınıfa sahip olduğunuzu söyleyelim. Bu salt okunur özelliklerin her biri için, veritabanında, mülkün değeri ile doldurulmasını istediğiniz karşılık gelen bir sütun var. Hala her özellik için eşanlamlısını kurmak için gidiyoruz, ancak nesneyi map yukarıdaki mapper uzantısını kullanan:

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) 
    }) 

cevap

1

Ben size sqlalchemy.orm kullanarak istediğini elde etmek mümkün olduğundan emin değilim. eşanlamlı sözcük. Şüphesiz, sqlalchemy'nin hangi örneklerin kirli olduğunu ve floş sırasında güncellenmesi gerektiğini nasıl izlediği gerçeğini vermedik. SessionExtensions üzerinde

(env)[email protected]:~/stackoverflow$ cat stackoverflow.py 

engine_string = '' 

from sqlalchemy import Table, Column, String, Integer, MetaData, create_engine 
import sqlalchemy.orm as orm 
import datetime 

engine = create_engine(engine_string, echo = True) 
meta = MetaData(bind = engine) 

foo_table = Table('foo', meta, 
    Column('id', String(3), primary_key=True), 
    Column('description', String(64), nullable=False), 
    Column('calculated_value', Integer, nullable=False), 
) 

meta.drop_all() 
meta.create_all() 

class MyExt(orm.interfaces.SessionExtension): 
    def before_commit(self, session): 
     for obj in session: 
      if isinstance(obj, Foo): 
       obj.calculated_value = datetime.datetime.now().second + 10 

Session = orm.sessionmaker(extension = MyExt())() 
Session.configure(bind = engine) 

class Foo(object): 
    def __init__(self, id, description): 
     self.id = id 
     self.description = description 

orm.mapper(Foo, foo_table) 

(env)[email protected]:~/stackoverflow$ ipython 
Python 2.5.2 (r252:60911, Jan 4 2009, 17:40:26) 
Type "copyright", "credits" or "license" for more information. 

IPython 0.10 -- An enhanced Interactive Python. 
?   -> Introduction and overview of IPython's features. 
%quickref -> Quick reference. 
help  -> Python's own help system. 
object? -> Details about 'object'. ?object also works, ?? prints more. 

In [1]: from stackoverflow import * 
2010-06-11 13:19:30,925 INFO sqlalchemy.engine.base.Engine.0x...11cc select version() 
2010-06-11 13:19:30,927 INFO sqlalchemy.engine.base.Engine.0x...11cc {} 
2010-06-11 13:19:30,935 INFO sqlalchemy.engine.base.Engine.0x...11cc select current_schema() 
2010-06-11 13:19:30,936 INFO sqlalchemy.engine.base.Engine.0x...11cc {} 
2010-06-11 13:19:30,965 INFO sqlalchemy.engine.base.Engine.0x...11cc select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where n.nspname=current_schema() and lower(relname)=%(name)s 
2010-06-11 13:19:30,966 INFO sqlalchemy.engine.base.Engine.0x...11cc {'name': u'foo'} 
2010-06-11 13:19:30,979 INFO sqlalchemy.engine.base.Engine.0x...11cc 
DROP TABLE foo 
2010-06-11 13:19:30,980 INFO sqlalchemy.engine.base.Engine.0x...11cc {} 
2010-06-11 13:19:30,988 INFO sqlalchemy.engine.base.Engine.0x...11cc COMMIT 
2010-06-11 13:19:30,997 INFO sqlalchemy.engine.base.Engine.0x...11cc select relname from pg_class c join pg_namespace n on n.oid=c.relnamespace where n.nspname=current_schema() and lower(relname)=%(name)s 
2010-06-11 13:19:30,999 INFO sqlalchemy.engine.base.Engine.0x...11cc {'name': u'foo'} 
2010-06-11 13:19:31,007 INFO sqlalchemy.engine.base.Engine.0x...11cc 
CREATE TABLE foo (
     id VARCHAR(3) NOT NULL, 
     description VARCHAR(64) NOT NULL, 
     calculated_value INTEGER NOT NULL, 
     PRIMARY KEY (id) 
) 


2010-06-11 13:19:31,009 INFO sqlalchemy.engine.base.Engine.0x...11cc {} 
2010-06-11 13:19:31,025 INFO sqlalchemy.engine.base.Engine.0x...11cc COMMIT 

In [2]: f = Foo('idx', 'foo') 

In [3]: f.calculated_value 

In [4]: Session.add(f) 

In [5]: f.calculated_value 

In [6]: Session.commit() 
2010-06-11 13:19:57,668 INFO sqlalchemy.engine.base.Engine.0x...11cc BEGIN 
2010-06-11 13:19:57,674 INFO sqlalchemy.engine.base.Engine.0x...11cc INSERT INTO foo (id, description, calculated_value) VALUES (%(id)s, %(description)s, %(calculated_value)s) 
2010-06-11 13:19:57,675 INFO sqlalchemy.engine.base.Engine.0x...11cc {'description': 'foo', 'calculated_value': 67, 'id': 'idx'} 
2010-06-11 13:19:57,683 INFO sqlalchemy.engine.base.Engine.0x...11cc COMMIT 

In [7]: f.calculated_value 
2010-06-11 13:20:00,755 INFO sqlalchemy.engine.base.Engine.0x...11cc BEGIN 
2010-06-11 13:20:00,759 INFO sqlalchemy.engine.base.Engine.0x...11cc SELECT foo.id AS foo_id, foo.description AS foo_description, foo.calculated_value AS foo_calculated_value 
FROM foo 
WHERE foo.id = %(param_1)s 
2010-06-11 13:20:00,761 INFO sqlalchemy.engine.base.Engine.0x...11cc {'param_1': 'idx'} 
Out[7]: 67 

In [8]: f.calculated_value 
Out[8]: 67 

In [9]: Session.commit() 
2010-06-11 13:20:08,366 INFO sqlalchemy.engine.base.Engine.0x...11cc UPDATE foo SET calculated_value=%(calculated_value)s WHERE foo.id = %(foo_id)s 
2010-06-11 13:20:08,367 INFO sqlalchemy.engine.base.Engine.0x...11cc {'foo_id': u'idx', 'calculated_value': 18} 
2010-06-11 13:20:08,373 INFO sqlalchemy.engine.base.Engine.0x...11cc COMMIT 

In [10]: f.calculated_value 
2010-06-11 13:20:10,475 INFO sqlalchemy.engine.base.Engine.0x...11cc BEGIN 
2010-06-11 13:20:10,479 INFO sqlalchemy.engine.base.Engine.0x...11cc SELECT foo.id AS foo_id, foo.description AS foo_description, foo.calculated_value AS foo_calculated_value 
FROM foo 
WHERE foo.id = %(param_1)s 
2010-06-11 13:20:10,481 INFO sqlalchemy.engine.base.Engine.0x...11cc {'param_1': 'idx'} 
Out[10]: 18 

Daha: sqlalchemy.orm.interfaces.SessionExtension -

Ama bu işlevselliği nasıl alabilirim başka yolu yok SessionExtensions (doldurulması gereken üstündeki engine_string değişkeni dikkat edin).

+1

İlginç.Bunu biraz farklı bir şekilde çözsem de, bunu cevap olarak işaretledim çünkü işe yarayacaktı. SQLAlchemy hakkında ne kadar çok şey öğrenirse, o kadar çok severim! –

5

Cevabınız ile düzenlediğiniz için teşekkürler, Jeff. Aynı problemi yaşadım ve kodunuzu kullanarak çözdüm, beyanname tabanı kullananlar için benzer bir şey. mapper argümanları ve eş anlamlı nasıl belirtileceği ararken bir kaç dakika kurtarabilir: DB hesaplanan değerleri kaydetmek için belirleyiciler gibi yöntemleri tanımlayalım Hibrid Özellikleri denilen SQLAlchemy destek şey, bir

from sqlalchemy.ext.declarative import declarative_base 

class Users(Base): 
    __tablename__ = 'users' 

    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    _calculated_value = Column('calculated_value', String) 

    __mapper_args__ = {'extension': UpdatePropertiesExtension(['calculated_value'])} 

    def __init__(self, name): 
    self.name = name 

    @property 
    def calculated_value(self): 
    self._calculated_value = "foobar" 
    return self._calculated_value 

    calculated_value = synonym('_calculated_value', descriptor=calculated_value) 
+0

Teşekkürler! Bana biraz zaman ayırdı. –

0

Daha yeni sürümleri.

Örnek kod vermek için yeterince iyi çözmeye çalıştığınız sorunu anladığımdan emin değilim, ancak bu sorunu Google üzerinden çağıran herkes için burada gönderiyor.

İlgili konular