2010-06-11 2 views
5

간단한 읽기 전용 속성에 대해 매핑하는 방법을 파악하고 데이터베이스에 저장할 때 해당 속성이 실행되도록 설정하려고합니다.SQLAlchemy - 읽기 전용 (또는 계산 된) 속성에 대해 매핑하는 방법

인위적인 예는이 사실을 분명히해야합니다. 먼저, 간단한 테이블 :

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

내가 내가여 session.commit를 (호출 할 때 나를 위해 calculated_value 컬럼에 삽입하는 읽기 전용 속성을 가진 클래스를 설정하고 싶은) ...

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의 워드 프로세서에 따르면, 나는 내가 너무 좋아이지도를 가정하고 생각 :

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

이의 문제가 _calculated_입니다 calculated_value 속성에 액세스 할 때까지 값은 None입니다. SQLAlchemy는 데이터베이스에 삽입 할 때 속성을 호출하지 않으므로 대신 None 값이 표시됩니다. "calculated_value"속성의 결과가 foo 테이블의 "calculated_value"열에 삽입되도록 매핑하는 올바른 방법은 무엇입니까?

확인 - 다른 사람이 같은 질문을하는 경우를 대비하여이 게시물을 편집 중입니다. 내가 마침내 MapperExtension을 사용했다.

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) 

을 그리고 이것은 당신이 사용하는 방법입니다 : 나를 확장의 사용과 함께 당신에게 더 나은 예를 들어 보겠습니다. 데이터베이스에 삽입하기 전에 시작해야하는 몇 가지 읽기 전용 속성이있는 클래스가 있다고 가정 해 보겠습니다. 여기서는 읽기 전용 속성 각각에 대해 속성 값으로 채워지는 데이터베이스의 해당 열을 가지고 있다고 가정합니다. 당신은 여전히 ​​각 속성에 대한 동의어를 설정하는거야,하지만 당신은 개체를 매핑 할 때 위의 매퍼 확장 사용

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

답변

1

나는 당신이 sqlalchemy.orm를 사용하여 원하는 것을 얻을 수 있어요 모르겠어요합니다. 동의어. 예를 들어, sqlalchemy가 어떤 인스턴스가 더럽고 플러시 중에 업데이트해야 하는지를 추적하는 방법을 알 수는 없습니다. SessionExtensions에

(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 

더 : sqlalchemy.orm.interfaces.SessionExtension -

그러나이 기능을 얻을 수있는 방법 다른 방법이 SessionExtensions는 (작성 될 필요가 상단에있는 engine_string 변수를주의).

+1

흥미 롭습니다.비록 내가 약간 다른 방식으로 이것을 해결했지만, 나는 그것이 작동해야하기 때문에 이것을 대답으로 표시했습니다. SQLAlchemy에 대해 더 많이 배울수록 더 좋아집니다! –

5

답변을 작성해 주셔서 감사합니다. Jeff. 나는 똑같은 문제를 가지고 있고 그것을 코드를 사용하여 해결했다. 여기에 선언적 기반을 사용하는 사람들과 비슷한 것이있다. 매퍼 인수 및 동의어를 지정하는 방법을 찾는 당신에게 몇 분을 절약 할 수 있습니다 당신은 DB에 계산 된 값을 저장하기위한 세터로 방법을 정의 할 수 하이브리드 속성이라고 SQLAlchemy의 지원 뭔가의

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

감사! 참으로 저를 구원해주었습니다. –

0

최신 버전을.

예제 코드를 제공하기 위해 충분히 해결해야하는 문제를 이해하고 있지만 Google을 통해이 코드를 우연히 발견하는 사람을 위해 여기에 게시합니다.

관련 문제