2017-01-18 15 views
3

私は、postgresql 9.5で追加された "新しい"機能を使用して、sqlalchemy coreを使用してアップサイトを行いたいと思います。それが実装されている間、私はかなり私のニーズに適応できない構文に混乱しています。ここ は私が行うことができるようにしたいもののサンプルコードです:postgresqlでsqlalchemyを使って適切なアップサンプリングを行うにはどうすればよいですか?

from sqlalchemy.ext.declarative import declarative_base 
Base = declarative_base() 
class User(Base): 
__tablename__ = 'test' 
a_id = Column('id',Integer, primary_key=True) 
a = Column("a",Integer) 

engine = create_engine('postgres://name:[email protected]/test') 
User().metadata.create_all(engine) 
meta = MetaData(engine) 
meta.reflect() 
table = Table('test', meta, autoload=True) 
conn = engine.connect() 

from sqlalchemy.dialects.postgresql import insert as psql_insert 
stmt = psql_insert(table).values({table.c['id']: bindparam('id'), 
      table.c['a']: bindparam('a')}) 
stmt = stmt.on_conflict_do_update(
    index_elements=[table.c['id']], 
    set_={'a': bindparam('a')}) 
list_of_dictionary = [{'id':1, 'a':1, }, {'id':2, 'a':2,}] 
conn.execute(stmt, list_of_dictionary) 

私はbasicly行の大部分を挿入すると、1つのIDがすでに使用されている場合、私は値でそれを更新したいです私はinitinalyが挿入したかった。 しかし私は、このエラーをスローSQLAlchemyの:

CompileError: bindparam() name 'a' is reserved for automatic usage in the VALUES or SET clause of this insert/update statement. Please use a name other than column name when using bindparam() with insert() or update() (for example, 'b_a'). 

それは(https://groups.google.com/forum/#!topic/sqlalchemy/VwiUlF1cz_oを参照)既知の問題ですが、私はlist_of_dictionaryのキーまたはあなたの名前のいずれかを変更する必要はありません任意の適切な答えを見つけることができませんでした列。

変数list_of_dictionaryのキーが挿入されたテーブルの列の名前であるかどうかに依存しない、一貫した動作を行う方法でstmtを構築する方法があるかどうかを知りたいのですがこれらの場合は間違いなく)。

答えて

4

これは私のためのトリックん:

from sqlalchemy import create_engine 
from sqlalchemy import MetaData, Table 
from sqlalchemy.dialects import postgresql 
from sqlalchemy.inspection import inspect 

def upsert(engine, schema, table_name, records=[]): 

    metadata = MetaData(schema=schema) 
    metadata.bind = engine 

    table = Table(table_name, metadata, schema=schema, autoload=True) 

    # get list of fields making up primary key 
    primary_keys = [key.name for key in inspect(table).primary_key] 

    # assemble base statement 
    stmt = postgresql.insert(table).values(records) 

    # define dict of non-primary keys for updating 
    update_dict = { 
     c.name: c 
     for c in stmt.excluded 
     if not c.primary_key 
    } 

    # cover case when all columns in table comprise a primary key 
    # in which case, upsert is identical to 'on conflict do nothing. 
    if update_dict == {}: 
     warnings.warn('no updateable columns found for table') 
     # we still wanna insert without errors 
     insert_ignore(table_name, records) 
     return None 


    # assemble new statement with 'on conflict do update' clause 
    update_stmt = stmt.on_conflict_do_update(
     index_elements=primary_keys, 
     set_=update_dict, 
    ) 

    # execute 
    with engine.connect() as conn: 
     result = conn.execute(update_stmt) 
     return result 
+0

は、私は必要なものであるstmt.excludedを、知っていませんでした。私は他の手であなたがプライマリキーを除外しようとしている理由を知りません、set = {cname:c for c stmt.excluded}は意図したとおりに動作するようです(プライマリを "更新"しても構いませんキー、それは定義上同じ値です) – Trolin

+0

ああ、それは良い点です。コードを少なくとももう少しエレガントにするでしょう。 –

+0

これは 'execute(query)'で動作しますか? 'query'はどこにも定義されません。それは 'execute(update_stmt)'でしょうか? – zebrainatree

関連する問題