2017-10-18 6 views
3

私は保留中の操作のテーブルを持つPostgres DBを持っています。列挙型の状態を持つ列挙型の操作の1つの列。一般的に、これは正常に動作します外部ファイルのSQLAlchemy列挙型

Base = declarative_base() 

class MyOperation(Base): 

    __tablename__ = 'operations' 

    id = Column(Integer, primary_key=True) 

    status = Column(Enum(MyStatus)) 
    status_message = Column(String) 
    status_time = Column(DateTime) 

    def __repr__(self): 
     return "<MyOperation(%s, %s, %s, %s)>" % \ 
      (self.id, self.status, self.status_time, self.status_message) 
# end class 

class AutoNumber(enum.Enum): 
    def __new__(cls): 
     value = len(cls.__members__) + 1 
     obj = object.__new__(cls) 
     obj._value_ = value 
     return obj 

class MyStatus(AutoNumber): 

    INITIAL =() 
    ACCEPTED =() 
    DENIED =() 
    ACK_PENDING =() 
    AUTHORIZED =() 
    ACTIVE =() 
    END =() 
    DELETED =() 
# end enum 

表は(もmyenum.py中)のようになります。私は、標準のPython(2.7)オートナンバー型と列挙型を、(myenum.py)を使用しました。 MyStatus(myoper.py)を定義し、同じファイルで、私はステータスを変更し、バックDBにそれを保存し、それが正常に動作することができます

def checkOper(oper): 
    oper.status = MyStatus.DENIED 
    oper.status_message = "failed check (internal)" 
    oper.status_time = datetime.datetime.utcnow() 

は、ここで私は(myoper.py以内)それを呼び出す方法です

checkOper(oper) 
    session.add(oper) 
    session.commit() 

これはすべて同じファイル(myoper.py)にあります。

しかし、私が外部関数にoperオブジェクトを渡し、ITが状態を変更すると、sqlalchemy.exc.StatementErrorが返されます。

はここで外部関数(myoper_test.py)です:

import datetime 
from myoper import MyStatus 

def extCheckOper(oper): 
    oper.status = MyStatus.DENIED 
    oper.status_message = "failed check (external)" 
    oper.status_time = datetime.datetime.utcnow() 

は、ここで私は(myoper.pyから)それを呼び出す方法は次のとおりです。

from myoper_test import extCheckOper 
    extCheckOper(oper) 
    session.add(oper) 
    session.commit() 

ここでスタックトレースがあります:

Traceback (most recent call last): 
    File "./myoper.py", line 120, in <module> 
    session.commit() 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 906, in commit 
    self.transaction.commit() 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 461, in commit 
    self._prepare_impl() 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 441, in _prepare_impl 
    self.session.flush() 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 2177, in flush 
    self._flush(objects) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 2297, in _flush 
    transaction.rollback(_capture_exception=True) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/langhelpers.py", line 66, in __exit__ 
    compat.reraise(exc_type, exc_value, exc_tb) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/session.py", line 2261, in _flush 
    flush_context.execute() 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/unitofwork.py", line 389, in execute 
    rec.execute(self) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/unitofwork.py", line 548, in execute 
    uow 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/persistence.py", line 177, in save_obj 
    mapper, table, update) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/orm/persistence.py", line 737, in _emit_update_statements 
    execute(statement, multiparams) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 945, in execute 
    return meth(self, multiparams, params) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/elements.py", line 263, in _execute_on_connection 
    return connection._execute_clauseelement(self, multiparams, params) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1053, in _execute_clauseelement 
    compiled_sql, distilled_params 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1121, in _execute_context 
    None, None) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1402, in _handle_dbapi_exception 
    exc_info 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/util/compat.py", line 203, in raise_from_cause 
    reraise(type(exception), exception, tb=exc_tb, cause=cause) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/base.py", line 1116, in _execute_context 
    context = constructor(dialect, self, conn, *args) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py", line 639, in _init_compiled 
    for key in compiled_params 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/engine/default.py", line 639, in <genexpr> 
    for key in compiled_params 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/sqltypes.py", line 1446, in process 
    value = self._db_value_for_elem(value) 
    File "/usr/local/lib/python2.7/dist-packages/sqlalchemy/sql/sqltypes.py", line 1354, in _db_value_for_elem 
    '"%s" is not among the defined enum values' % elem) 
sqlalchemy.exc.StatementError: (exceptions.LookupError) "MyStatus.DENIED" is not among the defined enum values [SQL: u'UPDATE operations SET status=%(status)s, status_message=%(status_message)s, status_time=%(status_time)s WHERE operations.id = %(operations_id)s'] [parameters: [{'status': <MyStatus.DENIED: 6>, 'status_time': datetime.datetime(2017, 10, 18, 20, 22, 44, 350035), 'status_message': 'failed check (external)', 'operations_id': 3}]] 

内部ファイルと外部ファイルの両方でタイプを検査しようとしましたが、その方法は<enum 'MyStatus'>と表示されています。

私を私が列挙.nameのにoper.statusを割り当てる場合は、仕事をすることことを、発見した:

def extCheckOper(oper): 
    oper.status = MyStatus.AUTHORIZED.name 
    oper.status_message = "authorized check (external)" 
    oper.status_time = datetime.datetime.utcnow() 

しかし、それは明らかにかなり醜いです。

だから私は間違っているのですか? SQLの錬金術を台無しにする外部ファイルに対して、定義されているファイルのMyStatusとは何が違うのですか?

+0

python2はenum型を組み込んでいません。enum34パッケージを使用していますか? – georgexsh

+0

はい、これはenum34です。 – TazMainiac

答えて

1

私はこの質問をSQL Alchemyメーリングリストに投稿して回答を得ました。 Link to thread

pythonについての "gotcha"のこの1つを紹介し、SQL Alchemyとはまったく関係がありません。参照先はExecuting Main Module Twiceです。

この特定のケースでは、スクリプトを実行すると、MyStatusが特定のID(その型のpythonハンドル)で作成されました。しかし、myoper_testがMyoperからMyStatusをインポートしたとき、別のIDを持つAGAINが作成されました。 extCheckOperがステータスフィールドにMyStatus値を割り当てたとき

だから、それはSQL錬金術はDBに保存しようとしたときに、演算子「ある」、SQL錬金術でDBマッピングを作成したよりも異なる MyStatusました(外部)MyStatusが(元の)MyStatusと異なるため、失敗しました。

複数の異なる回避策があります。一つの方法は、(main()関数にメインコードを出移動した後に)メインとして、コードを実行しないことです。

$ python -c "from myoper import main; import sys; main(*sys.argv[1:])" ext_check 1 

より良い解決策は完全にこの問題を回避することである - に外部にアウト呼び出すコードを移動します内部のテストスクリプト。 のコードは主にメインスクリプト内にあります(申し訳ありませんが、抵抗できませんでした:-))。

+0

それを学ぶことは素晴らしいことです。 – georgexsh