2016-03-21 8 views
0

私は実験的なプロトコル用のテーブルを持っています。これには他の多くのテーブルのための外部キーが含まれています(最も顕著なのはIncubationのエントリです)。構造はそのまま、次のようになります。SQLAlchemyのすべての関連テーブルを結合する

class DNAExtractionProtocol(Base): 
    __tablename__ = 'dna_extraction_protocols' 
    id = Column(Integer, primary_key=True) 
    code = Column(String, unique=True) 
    name = Column(String) 
    sample_mass = Column(Float) 
    mass_unit_id = Column(String, ForeignKey('measurement_units.id')) 
    mass_unit = relationship("MeasurementUnit", foreign_keys=[mass_unit_id]) 
    digestion_buffer_id = Column(String, ForeignKey("solutions.id")) 
    digestion_buffer = relationship("Solution", foreign_keys=[digestion_buffer_id]) 
    digestion_buffer_volume = Column(Float) 
    digestion_id = Column(Integer, ForeignKey("incubations.id")) 
    digestion = relationship("Incubation", foreign_keys=[digestion_id]) 
    lysis_buffer_id = Column(String, ForeignKey("solutions.id")) 
    lysis_buffer = relationship("Solution", foreign_keys=[lysis_buffer_id]) 
    lysis_buffer_volume = Column(Float) 
    lysis_id = Column(Integer, ForeignKey("incubations.id")) 
    lysis = relationship("Incubation", foreign_keys=[lysis_id]) 
    proteinase_id = Column(String, ForeignKey("solutions.id")) 
    proteinase = relationship("Solution", foreign_keys=[proteinase_id]) 
    proteinase_volume = Column(Float) 
    inactivation_id = Column(Integer, ForeignKey("incubations.id")) 
    inactivation = relationship("Incubation", foreign_keys=[inactivation_id]) 
    cooling_id = Column(Integer, ForeignKey("incubations.id")) 
    cooling = relationship("Incubation", foreign_keys=[cooling_id]) 
    centrifugation_id = Column(Integer, ForeignKey("incubations.id")) 
    centrifugation = relationship("Incubation", foreign_keys=[centrifugation_id]) 

    volume_unit_id = Column(String, ForeignKey('measurement_units.id')) 
    volume_unit = relationship("MeasurementUnit", foreign_keys=[volume_unit_id]) 

、ユニークcode帰属与え、私はパンダのデータフレームを取得したいと思います(というかシリーズ)私はの属性だけでなくいずれかを選択することができます"dna_extraction_protocols"テーブル内の対応するエントリだけでなく、関連するテーブルも参照してください。

は、私は現在でパンダのデータフレームを選択しています:

sql_query = session.query(DNAExtractionProtocol).join(DNAExtractionProtocol.digestion_buffer).filter(DNAExtractionProtocol.code == code) 
for item in sql_query: 
    pass 
mystring = str(sql_query) 
mydf = pd.read_sql_query(mystring,engine,params=[code]) 
print(mydf) 

しかし、これは私だけが関連するキーのIDを選択することができます。私はmydf["dna_extraction_protocols_mass_unit_id"]を選択することができます - しかし、私はまたmydf["dna_extraction_protocols_mass_unit_long_name"]を選択できるようにしたいと思い、"measurement_units"テーブルの上に以下の利用可能なキー与えられた:そのような質問に

class MeasurementUnit(Base): 
    __tablename__ = "measurement_units" 
    id = Column(Integer, primary_key=True) 
    code = Column(String, unique=True) 
    long_name = Column(String) 
    siunitx = Column(String) 

答えて

0

私がまだ出くわすことのできる最も非凡な方法は、the response to a related questionに基づいています。 SQLAlchemyのintrospect moduleを使うことができるようです。これにまだ残っている1つの注意点は、関連するテーブルのすべてのレベルで、参加する必要がある場合は、最も小さい検査呼び出しのためにネストされたforループを追加する必要があるということです。メインテーブルの *すべての関連テーブルメインテーブルのすべての関連表の *すべての関連表を:ここで

が加わり例です

cols = [] 
joins = [] 
insp = inspection.inspect(DNAExtractionProtocol) 
for name, col in insp.columns.items(): 
    cols.append(col.label(name)) 
for name, rel in insp.relationships.items(): 
    alias = aliased(rel.mapper.class_, name=name) 
    joins.append((alias, rel.class_attribute)) 
    for col_name, col in inspection.inspect(rel.mapper).columns.items(): 
     #the id column causes double entries, as it is mapped once on the parent table (related_table_id) and once on the child table (table_id) 
     if col.key != "id": 
      aliased_col = getattr(alias, col.key) 
      cols.append(aliased_col.label("{}_{}".format(name, col_name))) 

    sub_insp = inspection.inspect(rel.mapper.class_) 
    for sub_name, sub_rel in sub_insp.relationships.items(): 
     if "contains" not in sub_name: 
      sub_alias = aliased(sub_rel.mapper.class_, name=name+"_"+sub_name) 
      joins.append((sub_alias, sub_rel.class_attribute)) 
      for sub_col_name, sub_col in inspection.inspect(sub_rel.mapper).columns.items(): 
       print(sub_alias, sub_col.key, '###') 
       #the id column causes double entries, as it is mapped once on the parent table (related_table_id) and once on the child table (table_id) 
       if sub_col.key != "id": 
        sub_aliased_col = getattr(sub_alias, sub_col.key) 
        cols.append(sub_aliased_col.label("{}_{}_{}".format(name, sub_name, sub_col_name))) 

sql_query = session.query(*cols).select_from(DNAExtractionProtocol) 
for join in joins: 
    sql_query = sql_query.outerjoin(*join) 
sql_query = sql_query.filter(DNAExtractionProtocol.code == code) 

私はIDを除外するためのハックを追加する必要がありましたこれらの列は、名前付けスキームが代わりに編集されている場合にも保持できます。

1

古典的な答えは、SQLビューを作成することです。

ビューは動的仮想テーブルに似ています。クエリでは、テーブル名の代わりにビュー名を使用し、DBMSはビューで定義されたクエリを実行してビューのクエリ行を生成します。したがって、ビューが作成された時点ではなく、ビューにアクセスした時点でテーブルのデータに基づいて行が表示されます。あなたはこのような

CREATE VIEW PROT_WITH_UNITS AS 
    SELECT * FROM dna_extraction_protocols P 
      JOIN measurement_units M 
      ON P.volume_unit = M.id 

として声明で、このビューを作成します

これは(私があることを推測するもの)に事前参加し、あなたに必要な外部キーを両方のテーブルのすべての列を持つビューを提供します。

もし定義が間違っていると、テーブルのようにビューを削除することができるので、最終的にそこに行かなければなりません。

+0

ありがとうございます、それはすでに大きな前進ですが、 'measurement_units'テーブルは単なる一例にすぎません。私は自分のエントリがリンクしているすべてのテーブルに参加したいと思います。 - 手で指定する必要はありません。これは可能ですか? – TheChymera

+0

これはquerryのようです: 'sql_query = session.query(tables [table])。オプション(eagerload(tables [table] .digestion_buffer))フィルタ(tables [table] .code == code)'関連するテーブルをそれぞれ確保する必要がありますか? – TheChymera

+0

ビュー内の巨大なselectステートメントに必要なすべてのテーブルを指定します。一度。高速なデータアクセスが柔軟性よりも重要である場合、ビューを取得し、その結果を大きなテーブルに格納することによって、ビューをマテリアル化することができます。 – holdenweb

関連する問題