質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.99%

sqlalchemyでqueryを使うとmapperがhas no propertyを吐く理由が分からない

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,027

sasaya

score 10

sqlalchemyを使い、データベースからデータを取得する時にmapperがhas no property と
エラーを吐いてしまいます。
公式チュートリアルに倣って書いているはずなのですが何が問題なのか理解しかねております。
どこの記述が問題となっているのか教えていていただけ無いでしょうか。

python 3.6.2
sqlalchemy '1.1.13'

エラー発生している部分はこちらになります。

stocks = session.query(StockData)
print(stocks)
print(type(stocks))


Traceback はこちらになります。

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\mapper.py in get_property(self, key, _configure_mappers)
   1858         try:
-> 1859             return self._props[key]
   1860         except KeyError:

KeyError: 'StockData'

During handling of the above exception, another exception occurred:

InvalidRequestError                       Traceback (most recent call last)
<ipython-input-17-7e886190d051> in <module>()
      3 session = Session()
      4 
----> 5 stocks = session.query(StockData)
      6 print(stocks)
      7 print(type(stocks))

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\session.py in query(self, *entities, **kwargs)
   1360         :class:`.Session`."""
   1361 
-> 1362         return self._query_cls(entities, self, **kwargs)
   1363 
   1364     @property

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\query.py in __init__(self, entities, session)
    137         self.session = session
    138         self._polymorphic_adapters = {}
--> 139         self._set_entities(entities)
    140 
    141     def _set_entities(self, entities, entity_wrapper=None):

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\query.py in _set_entities(self, entities, entity_wrapper)
    148             entity_wrapper(self, ent)
    149 
--> 150         self._set_entity_selectables(self._entities)
    151 
    152     def _set_entity_selectables(self, entities):

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\query.py in _set_entity_selectables(self, entities)
    178                         aliased_adapter
    179                     )
--> 180                 ent.setup_entity(*d[entity])
    181 
    182     def _mapper_loads_polymorphically_with(self, mapper, adapter):

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\query.py in setup_entity(self, ext_info, aliased_adapter)
   3583         self.selectable = ext_info.selectable
   3584         self.is_aliased_class = ext_info.is_aliased_class
-> 3585         self._with_polymorphic = ext_info.with_polymorphic_mappers
   3586         self._polymorphic_discriminator = \
   3587             ext_info.polymorphic_on

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\util\langhelpers.py in __get__(self, obj, cls)
    762         if obj is None:
    763             return self
--> 764         obj.__dict__[self.__name__] = result = self.fget(obj)
    765         return result
    766 

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\mapper.py in _with_polymorphic_mappers(self)
   1946     def _with_polymorphic_mappers(self):
   1947         if Mapper._new_mappers:
-> 1948             configure_mappers()
   1949         if not self.with_polymorphic:
   1950             return []

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\mapper.py in configure_mappers()
   2870                 if not mapper.configured:
   2871                     try:
-> 2872                         mapper._post_configure_properties()
   2873                         mapper._expire_memoizations()
   2874                         mapper.dispatch.mapper_configured(

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\mapper.py in _post_configure_properties(self)
   1763 
   1764             if prop.parent is self and not prop._configure_started:
-> 1765                 prop.init()
   1766 
   1767             if prop._configure_finished:

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\interfaces.py in init(self)
    182         
    183         self._configure_started = True
--> 184         self.do_init()
    185         self._configure_finished = True
    186 

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\relationships.py in do_init(self)
   1655         self._check_cascade_settings(self._cascade)
   1656         self._post_init()
-> 1657         self._generate_backref()
   1658         self._join_condition._warn_for_conflicting_sync_targets()
   1659         super(RelationshipProperty, self).do_init()

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\relationships.py in _generate_backref(self)
   1879 
   1880         if self.back_populates:
-> 1881             self._add_reverse_property(self.back_populates)
   1882 
   1883     def _post_init(self):

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\relationships.py in _add_reverse_property(self, key)
   1595 
   1596     def _add_reverse_property(self, key):
-> 1597         other = self.mapper.get_property(key, _configure_mappers=False)
   1598         self._reverse_property.add(other)
   1599         other._reverse_property.add(self)

~\Anaconda3\envs\untitled\lib\site-packages\sqlalchemy\orm\mapper.py in get_property(self, key, _configure_mappers)
   1860         except KeyError:
   1861             raise sa_exc.InvalidRequestError(
-> 1862                 "Mapper '%s' has no property '%s'" % (self, key))
   1863 
   1864     def get_property_by_column(self, column):

InvalidRequestError: Mapper 'Mapper|History|history' has no property 'StockData'

全文はこちらになります。

import sqlalchemy
from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()
from sqlalchemy import Column, Integer, String, Float, Date, DateTime, SmallInteger, Boolean
from sqlalchemy import ForeignKey
from sqlalchemy.sql import select, text
from sqlalchemy.orm import relationship
import datetime
import pandas as pd
import numpy as np
import os

class StockData(Base):
    """銘柄の情報を持つクラス"""
    __tablename__ = 'stock'
    code = Column(Integer, primary_key=True)  # 銘柄コード
    name = Column(String(64), nullable=False)  # 銘柄の名前
    category_id = Column(Integer, nullable=True, index=True)  # 業種
    activated = Column(Boolean, default=False)  # backtest対象か否か
    created_at = Column(DateTime, default=datetime.now())  # 作成日時
    updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now())  # 更新日時

class History(Base):
    """価格情報を持つクラス"""
    __tablename__ = 'history'
    code = Column(Integer,ForeignKey('stock.code'), primary_key=True, )  # 銘柄コード
    date = Column(Date, primary_key=True, unique=False)  # 日付
    #raw_close_price = Column(Float, nullable=False)  # 終値(株式分割調整前)
    Open = Column(Float, nullable=False)  # 始値
    High = Column(Float, nullable=False)  # 高値
    Low = Column(Float, nullable=False)  # 安値
    Close = Column(Float, nullable=False)  # 終値
    Volume = Column(Integer, nullable=False)  # 出来高

    stock = relationship('StockData', back_populates='history')


StockData.code = relationship("History", order_by=History.code, back_populates="StockData")
Base.metadata.create_all(engine)

# データベースへのデータ挿入 pandas.to_sqlを使用
files = os.listdir('dd')
for file in files:
    if(os.path.isfile('dd/' + file)):
        hist = pd.read_csv('dd/' + file,
                           names=["date", "Open", "High", "Low", "Close", "Volume"],
                           usecols=range(6))
        stk = pd.read_csv('dd/' + 'info' + '/' + file, names=["code", "name", "category_id"])
        codes = np.repeat(np.array(stk["code"]), (len(hist["date"])))
        hist['code'] = codes
        lst = hist.columns.tolist()
        lst.remove('code')
        lst.insert(0, 'code')
        hist = hist[lst]
        # バックテスト対象,作成日時,更新日時のパラメータの追加
        stk['activated'] = True
        stk['created_at'] = datetime.now()
        stk['updated_at'] = datetime.now()

        stk.to_sql('stock', engine, index=False, if_exists='append')
        hist.to_sql('history', engine, index=False, if_exists='append')

stocks = session.query(StockData)
print(stocks)
print(type(stocks))
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

SQLAlchemyにおいて、子になるテーブル(History)から親になるテーブル(StockData)に対してリレーションを張る場合は、親のテーブル定義にも子になるテーブルからリレーションが張られることを知らせる、以下のコードが必要になります。

class StockData(Base):
    """銘柄の情報を持つクラス"""
    __tablename__ = 'stock'
    code = Column(Integer, primary_key=True)  # 銘柄コード
    name = Column(String(64), nullable=False)  # 銘柄の名前
    category_id = Column(Integer, nullable=True, index=True)  # 業種
    activated = Column(Boolean, default=False)  # backtest対象か否か
    created_at = Column(DateTime, default=datetime.now())  # 作成日時
    updated_at = Column(DateTime, default=datetime.now(), onupdate=datetime.now())  # 更新日時

    history = relationship('History')  # << 追加   

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/27 16:53 編集

    データベースをmemoryではなく外部に出力して、データベースの中身の行と列が意図したとおりになっていることをPycharmでの確認を行い,

    更に以下の直接SQL文を実行した結果の確認を行って、データベースの中身が意図したものになっている事の確認を行っております。

    rows = engine.execute('SELECT * FROM stock')
    for row in rows:
    print(row)

    2018-01-27 16:57:37,372 INFO sqlalchemy.engine.base.Engine SELECT * FROM stock
    2018-01-27 16:57:37,430 INFO sqlalchemy.engine.base.Engine ()
    (1301, '(株)極洋', '水産・農林業', 1, '2018-01-27 15:10:38.578098', '2018-01-27 15:10:38.579101')
    (1305, 'ダイワ 上場投信-トピックス', 'ETF', 1, '2018-01-27 15:10:39.149910', '2018-01-27 15:10:39.150412')
    (1306, 'TOPIX連動型上場投資信託', 'ETF', 1, '2018-01-27 15:10:39.675060', '2018-01-27 15:10:39.675060')
    (1308, '上場インデックスファンドTOPIX', 'ETF', 1, '2018-01-27 15:10:40.105236', '2018-01-27 15:10:40.105236')

    キャンセル

  • 2018/01/27 21:00

    プログラム内の StockData.code = relationship("History", order_by=History.code, back_populates="StockData") は不要かと思います。 これが StockData.code を書き換えているので
    予期しないエラーが発生しているのだと思います。

    キャンセル

  • 2018/01/27 21:09 編集

    確かに該当部分をコメントアウトしたところ正常動作いたしました。大変ありがとうございます。

    stocks = session.query(StockData.code)
    for stock_res in stocks.all():
    print(stock_res.code)

    2018-01-27 21:08:08,662 INFO sqlalchemy.engine.base.Engine BEGIN (implicit)
    2018-01-27 21:08:08,664 INFO sqlalchemy.engine.base.Engine SELECT stock.code AS stock_code
    FROM stock
    2018-01-27 21:08:08,666 INFO sqlalchemy.engine.base.Engine ()
    1305
    1306
    1308
    1301

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる