無料でWebアプリを作ろう_002.js

  • 11◆jG/Re6aTC.23/04/27(木) 19:24:36

    ## 概要

    タイトルの通りWebアプリを作ろうと四苦八苦するスレ主の様子を観察し意見やアドバイスまたは茶々をいれてくれると嬉しいスレです

    一人でやってるとサボったりエタったりしそうなのでスレ立ててみました

    2スレ目

  • 21◆jG/Re6aTC.23/04/27(木) 19:25:23
  • 31◆jG/Re6aTC.23/04/27(木) 19:28:20

    あらすじ

    ローカルのWeb画面にベタ書きのデモデータを表示するところまでできたよ

    次はAPIからデモデータを取得したい!

  • 41◆jG/Re6aTC.23/04/27(木) 19:29:18

    あ、質問ご意見アドバイス大歓迎です

    「○○」てどういう意味?
    から
    「この構成イケてないんじゃない?」
    まで遠慮なくどうぞ

  • 51◆jG/Re6aTC.23/04/27(木) 19:54:47

    さて

    hello worldができたら同じ要領でデモデータを返すようにAPIを作成します

    こんな感じに追記

  • 61◆jG/Re6aTC.23/04/27(木) 20:00:39

    軽く説明すると

    @app.get("/fridge")

    で「(APIのルートパス)/fridge にGETメソッドでリクエストが来た時にすぐ下に書いてある関数を実行」という意味になる

    その下の関数名「get_fridge」は適当で良い

    引数とかも指定できるけどここではまだ定義しない

    そんで関数の中の処理で返すデータをjson配列で準備して最終的にreturnしている

  • 71◆jG/Re6aTC.23/04/27(木) 21:26:08

    飯食ってました

    続きいきます

  • 81◆jG/Re6aTC.23/04/27(木) 21:28:21

    apiの挙動を確認してみよう

    対応するパスは

    localhost/api/fridge

    になるはずなのでブラウザで叩く

    ヨシ!
    ちゃんと結果が返ってきました

  • 9二次元好きの匿名さん23/04/27(木) 21:28:51

    おぉ!がんばれー!

  • 101◆jG/Re6aTC.23/04/27(木) 21:38:37

    今のままでも動きますがここでプラスアルファの調整をします

    fastapiはコンテナでルートパスで動いていますがこれを/apiに合わせます

    つまりこうこうだったのを

  • 111◆jG/Re6aTC.23/04/27(木) 21:40:20

    こうします

    URLで相対参照しているファイルなんかがあるとリバプロの前と後でパスの扱いが変わったりしてしまうのでなるべく合わせると安心できます

  • 121◆jG/Re6aTC.23/04/27(木) 21:46:42

    これにはfastapi側とリバプロ側の2個所の設定変更が必要

    まずはfatapi側
    fastapiを起動するときに --root-path オプションで設定できるらしい
    公式イメージは DockerfileのCMDで起動時のオプションを指定しているようなので公式が事前に設定している

    CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]

    ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80", "--root-path", "/api" ]

    のように追記すれば良いはず
    docker-composeではこのようにするとイメージのCMDを上書きできる

  • 131◆jG/Re6aTC.23/04/27(木) 21:47:56

    次にリバプロの定義

    対応するパスの末尾に /api/ を追記すればいい

  • 141◆jG/Re6aTC.23/04/27(木) 21:53:40

    commandの上書きは

    ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80", "--root-path", "/api" ]

    のようにmain:app でないといけなかったっぽいので修正

  • 151◆jG/Re6aTC.23/04/27(木) 21:59:35

    あれ、上手くいかんな

    このへんややこしいっぽい

  • 161◆jG/Re6aTC.23/04/27(木) 22:01:47

    なるほど、リバプロ側の追記はいらなかった模様

    戻せ戻せ

  • 171◆jG/Re6aTC.23/04/27(木) 22:03:36

    あとFastAPIのルートパスオプションも戻す

    起動時でもいけるがプログラム内でも設定できるらしい

    docker-composeのcommandの記述も戻してFastAPI側のコードをこのように追記

  • 181◆jG/Re6aTC.23/04/27(木) 22:06:08

    これで fridge apiも戻ったうえFastAPIの便利な機能も使えるようになった


    localhost

    にアクセスするとプログラム内で定義した機能を確認できるWeb画面に遷移する


    これはFastAPIがソースコードから自動生成してくれる


    ソースコードをコメント含めちゃんと書いていれば仕様書として使えるレベルのものを自動生成してくれるのだ

  • 191◆jG/Re6aTC.23/04/27(木) 22:06:50

    いかんURLが変換されてしまったな

    localhost/api/docs

    にアクセスするとプログラム内で定義した機能を確認できるWeb画面に遷移する

    と書いてあった

  • 201◆jG/Re6aTC.23/04/27(木) 22:09:04

    作っていたfridge apiについて見てみる

    コメント文も反映されている(今回は表示されてないが入力可能なパラメータがあればここに表示される)

    またこの画面からAPIのテストもできる

    Try it outをクリック

  • 211◆jG/Re6aTC.23/04/27(木) 22:10:45

    その後表示されるExecuteボタンを押すとこのように実行結果が確認できる

    テストもできる仕様書!素晴らしい!

    こういった便利な機能を使いたかったのでルートパスを調整したのだ

  • 221◆jG/Re6aTC.23/04/27(木) 22:53:24

    APIが用意できたのでWebixと連携していく

    テーブル部品のURL部分をAPIから取得するように置き換えていくぞ

  • 231◆jG/Re6aTC.23/04/27(木) 23:00:51

    ここの値を

    叡智ttp://localhost/api/fridge

    に置き換えればいいのだがここでワンポイント
    そうではなくて

    `${location.protocol}//${location.host}/api/fridge`

    のように書く

    なぜかというと localhost... はローカル環境限定のURLなので最終的にインターネットに公開すると使えなくなってしまう

    `${location.protocol}//${location.host}`

    と書くと「今ブラウザで開いているプロトコル//ホスト名」(=今ブラウザで開いているURL)になるため、ローカルでもインターネット上でも共通して使える記述になる

  • 241◆jG/Re6aTC.23/04/27(木) 23:03:54

    なんとこれだけでWebAPIと連携できてしまう

    JavascriptでWebAPI連携を書いたことがある人ならわかるでしょう

    かつてJavascriptのWebAPI連携は非同期処理によりカッコを何重にもネストした複雑なコードを書かなければならなかった...

  • 251◆jG/Re6aTC.23/04/27(木) 23:06:03

    Webixの利用によりたった1行でWebAPIからデータを取得し表示できるようになったのだ!

    画面表示ヨシ!

    ...

    デモデータと内容同じだからちゃんとWebAPIから取得できてるか確証がもてんな...

  • 261◆jG/Re6aTC.23/04/27(木) 23:11:34

    返ってくるデモデータを増やして反映されるか見てみよう

  • 271◆jG/Re6aTC.23/04/27(木) 23:14:04

    しっかり反映されてますね

    ヨシ!

  • 281◆jG/Re6aTC.23/04/27(木) 23:20:34

    キリがいいので今日はここまでです

    次はいよいよインターネット上にあるDBとの連携ですね

    お疲れ様でしたー

    ご意見感想質問アドバイスお待ちしております

  • 291◆jG/Re6aTC.23/04/28(金) 09:57:02

    GWに入るともう少しがっつり進められるかなぁ

  • 301◆jG/Re6aTC.23/04/28(金) 18:27:52

    続きやっていきまーす

  • 311◆jG/Re6aTC.23/04/28(金) 18:30:53

    DBにOracleCloudを選定した理由は前スレでも説明したけど大きく3つ

    - 無料
    - 20GBも使える
    - 非アクティブ7日までOK(データ自体は3カ月まで)

  • 321◆jG/Re6aTC.23/04/28(金) 18:36:17

    アカウント作成は事前調査でしておいたので割愛(リクエストがあれば解説します)

    さっそくDBのインスタンスを作ろう

    ログイン後のトップ画面から


    「メニューボタン」->「Oracle Database」->「Autonomous Database」


    と進む

  • 331◆jG/Re6aTC.23/04/28(金) 18:39:45

    基本情報を適当に指定

  • 341◆jG/Re6aTC.23/04/28(金) 18:42:39

    ワークロード・タイプとやらを選択する
    DBの得意分野を決めるところらしいがとりあえず基本っぽい「データ・ウェアハウス」を選択

    「デプロイメント・タイプ」は「共有インフラストラクチャ」を選択
    おそらく無料枠はこれしか選べない

  • 351◆jG/Re6aTC.23/04/28(金) 18:48:07

    データベースの構成セクションで「Always Freeの構成オプションのみを表示」のトグルをONにする
    これによって無料枠の構成に設定してくれる

    7日間連続でアクティビティがなかった場合に停止し、さらに3カ月間ないと削除される旨が書かれてる
    ここはその他は選択肢が固定なので後はそのままでいい
    無料で20DB使わせてくれるたあ太っ腹だ
    少しでも気軽に使ってもらって他のクラウドユーザーを引き込みたいという意図が見える

  • 361◆jG/Re6aTC.23/04/28(金) 18:50:37

    「管理者資格証明の作成」セクションでDBのADMINユーザーのパスワードを設定する

    どうせ普通にキーボードで入力するようなことはないのでできる限り複雑にしよう

  • 371◆jG/Re6aTC.23/04/28(金) 18:55:32

    「ネットワーク・アクセスの選択」ではDBにアクセス可能なIPを限定できたりするようだ
    しかし最終的にk8s上で動かすことを想定するとIPはコロコロ変わる可能性がある
    なのでここは「全ての場所から...」にする

    また「相互TLS (mTLS)認証が必要」のチェックはつけたままにする
    これにするとパスワードに加えてお互いに証明書を持っていないとアクセスできなくなるんだとか

  • 381◆jG/Re6aTC.23/04/28(金) 18:57:47

    あとは全てデフォルトで良さそうなので「Autonomous Databaseの作成」をクリック

  • 39二次元好きの匿名さん23/04/28(金) 18:58:47

    このレスは削除されています

  • 401◆jG/Re6aTC.23/04/28(金) 19:00:47

    するとDBのプロビジョニング(構築)が始まった

    待てしか

  • 411◆jG/Re6aTC.23/04/28(金) 19:01:55

    1分くらいで使用可能になった

    早いな

  • 421◆jG/Re6aTC.23/04/28(金) 19:05:24

    あとは事前にDB接続に必要な証明書ファイル(ウォレット)をダウンロードしておく

    「データベース接続」をクリック

  • 43二次元好きの匿名さん23/04/28(金) 19:06:50

    証明書とdnsはどうするんだ?

  • 441◆jG/Re6aTC.23/04/28(金) 19:06:52

    あとはここからDLできるのだが...

    あれ?

    使おうと思ってた方式だとウォレットいらないって書いてあるな...

  • 451◆jG/Re6aTC.23/04/28(金) 19:09:14

    >>43

    SSL証明書とDNSのことですかね?


    使おうと思ってるOkutetoが提供してくれるっぽいのでとりあえずはそれを使う想定です

    もし難しそうだったら...


    DNSはやらなくていいと思ってます。どうせ個人利用ですすIPでいいかなと

    証明書はZeroSSLを使おうかと

  • 461◆jG/Re6aTC.23/04/28(金) 19:11:50

    このへんの接続文字列さえあればいいのかな...?

    よし、ここでオリチャー発動!

    ウォレットはDLしないことにします

  • 47二次元好きの匿名さん23/04/28(金) 19:11:58

    >>45

    ありがとうございます

    Dockerまわりよくわからんかったのでサービスしれて助かりました

    確かに個人公開ならIPベースでもいいですね

    引き続き頑張ってください

  • 481◆jG/Re6aTC.23/04/28(金) 19:14:20

    >>47

    こちらこそ質問ありがとう!

    私もはじめて触るサービスなので先に触って使い勝手を報告してくれてもいいのよ?

  • 491◆jG/Re6aTC.23/04/28(金) 19:15:15

    ちなみに接続先が3つあるのは接続先によって処理の優先順位をコントロールするためらしい


    とりあえずmediumでいいのかな


    https://blogs.oracle.com/oracle4engineer/post/adb-services-shares-201905

  • 50二次元好きの匿名さん23/04/28(金) 19:17:28

    このレスは削除されています

  • 511◆jG/Re6aTC.23/04/28(金) 19:39:04

    なんかインターネット接続の調子悪いな

    PC再起動がてら飯食ってきます

  • 521◆jG/Re6aTC.23/04/28(金) 20:35:47

    再開しまーす

  • 531◆jG/Re6aTC.23/04/28(金) 20:38:24

    DBの準備がとりあえずできたのであとはクライアント側を準備する

    FastAPIで動かすことを想定しているのでそのコンテナイメージの上で開発を進めていこう

    ただFastAPIのコンテナはデフォルトだと/app/main.pyを自動実行しようとするのでコーディング環境には向かない

    このようにentrypointとcommandを上書きすることでfastapiの自動起動を防ぎただ何もしないコンテナとして起動させることができる

  • 541◆jG/Re6aTC.23/04/28(金) 20:42:56

    あとは起動してVSCodeからコンテナに接続すれば開発環境の出来上がり

    Python拡張の動作も確認OK

  • 551◆jG/Re6aTC.23/04/28(金) 20:45:28
  • 561◆jG/Re6aTC.23/04/28(金) 20:49:36

    モジュール本体を以下のコマンドでインストール

    python -m pip install oracledb --upgrade

  • 571◆jG/Re6aTC.23/04/28(金) 20:51:42

    こんな感じでコネクションを定義

  • 581◆jG/Re6aTC.23/04/28(金) 20:52:36

    DSN記述はさっき控えておいたのを使うらしい

  • 591◆jG/Re6aTC.23/04/28(金) 20:58:03

    パラメータを変数化してSQL実行部分を用意

    テーブルの一覧を取得するSQLでテストする

    どうかな?

  • 601◆jG/Re6aTC.23/04/28(金) 21:00:40

    む、エラーで繋げない

    とりあえず調べてみる

  • 611◆jG/Re6aTC.23/04/28(金) 21:06:02

    やっぱりウォレットは必要っぽい

    インスタンスウォレットをDLする

  • 621◆jG/Re6aTC.23/04/28(金) 21:07:48

    ウォレットのパスワードを設定してDL

    これはウォレットのパスワードでありDBユーザーのパスワードではないことに注意

  • 631◆jG/Re6aTC.23/04/28(金) 21:11:44

    zipファイル形式でDLできた

  • 641◆jG/Re6aTC.23/04/28(金) 21:19:06

    zipファイルを解凍しその中から「tnsnames.ora」と「ewallet.pem」をpythonファイルから参照できるコンテナ側のパスにコピーする

  • 651◆jG/Re6aTC.23/04/28(金) 21:21:01

    dsnを「tnsnames.ora」内のmedium接続名に変更し、新たにwalletの配置ディレクトリパスとwalletパスワードを指定する必要が出てきた

  • 661◆jG/Re6aTC.23/04/28(金) 21:21:52

    コネクションの方も定義した変数を参照するように変更していざ実行!

  • 671◆jG/Re6aTC.23/04/28(金) 21:23:34

    おお!色々返ってきた!

    ヨシ!

    だからウォレットをDLする必要があったんですね(ガバチャー)

  • 681◆jG/Re6aTC.23/04/28(金) 21:28:52

    目標としていたここの接続ができたことになります


    あなたOracleDBに関する知識の向上を感じた! ワァオー *保存*


  • 691◆jG/Re6aTC.23/04/28(金) 21:33:28

    ここまでいったらDB設計などに入りたいですがここでひと手間かけます
    ORマッパーを使いたい なぜかというと...

    SQLを書いてDBは操作できるようになったのですがDBにはSQLの「方言」的要素があります
    OracleDBでは使えてPostgresでは使えないSQL構文があったりするわけですね

    今回のコンセプトが無料であることを掲げている以上、サービスが急に無料をやめる可能性がありDBの種類を変えざるを得ないケースが発生するかもしれません
    そうなったときにコード内のSQL記述を変更するのは大変!

  • 701◆jG/Re6aTC.23/04/28(金) 21:36:13

    そこで役立つのがORM

    ORMを使うと共通の記述で異なるDBにも同じ操作を実行することができるようになります

    またSQLを使わないのでより「Pythonコード的」に書くことができる
    Pythonコード内に別の言語形態の記述が入って可読性を下げてしまうのを回避します

  • 711◆jG/Re6aTC.23/04/28(金) 21:46:17

    Pythonで有名なORMに「SQLAlchemy」があるのでこれを使う


    (本当はfastapiと連携がしやすいらしい「sqlmodel」なるものを使いたかったがoracledbモジュールに対応していないっぽいので断念)


    SQLAlchemy - The Database Toolkit for Pythonwww.sqlalchemy.org
  • 721◆jG/Re6aTC.23/04/28(金) 21:46:30

    下のコマンドでsqlalchemyをインストール

    python -m pip install sqlalchemy

  • 731◆jG/Re6aTC.23/04/28(金) 21:47:24

    import記述を追加して

  • 741◆jG/Re6aTC.23/04/28(金) 21:48:35

    このようにコネクションを作りSQLを実行する

    (本来はSQLは使用しないが接続確認のため)

  • 751◆jG/Re6aTC.23/04/28(金) 21:49:23

    実行する

    結果が返ってきてるっぽいのでヨシ!

  • 761◆jG/Re6aTC.23/04/28(金) 21:52:39

    あとはいつまでもDBユーザーをADMINのまま使うのも気持ち悪いので開発用ユーザーを作る

    Oracle CloudのWebUIから「データベース・アクション」をクリック

    ブラウザでポップアップを許可しないと正常に処理されないので気を付けよう(一敗)

  • 771◆jG/Re6aTC.23/04/28(金) 21:54:31

    こんな画面に遷移する

    ここからSQLを実行する画面に行けたりもするらしい

  • 781◆jG/Re6aTC.23/04/28(金) 21:55:42

    ハンバーガーメニューから「データベース・ユーザー」を選択

  • 79二次元好きの匿名さん23/04/28(金) 21:56:47

    コードやってて困るもの
    「エラーを吐かないエラー」
    いっそエラーを吐いてくれ…吐け!!

  • 80二次元好きの匿名さん23/04/28(金) 21:57:50

    >>71

    こんなのあるんだなぁ

  • 811◆jG/Re6aTC.23/04/28(金) 21:58:28

    「ユーザーの作成」をクリック

  • 82二次元好きの匿名さん23/04/28(金) 21:59:55

    pythonの開発環境は何を使ってるんです?
    spyder?

  • 831◆jG/Re6aTC.23/04/28(金) 22:03:55

    VSCode + Pytohn拡張 + docker

    ですね

    逆にspyderなんて

  • 841◆jG/Re6aTC.23/04/28(金) 22:07:07

    こんな感じでいいのかな

    表領域=データが保存できる領域
    らしい
    とりあえず1Gにして足りなくなったら増やそう

  • 851◆jG/Re6aTC.23/04/28(金) 22:10:28

    大切なのがロールを付与することらしい

    これをしないとユーザーは作成できても操作ができないユーザーが出来上がる

    基本機能がまとまってるらしい「DWROLE」にチェックを入れて「ユーザーの作成」をクリック

  • 86二次元好きの匿名さん23/04/28(金) 22:10:56

    このレスは削除されています

  • 871◆jG/Re6aTC.23/04/28(金) 22:12:11

    無事に作成されたようだ

  • 88二次元好きの匿名さん23/04/28(金) 22:12:20

    >>83

    データサイエンスとかやってると良く使われる便利環境です(spyder)

    実際、コンソールやデータの流用がしやすい点等が優秀なのでデータ解析分野では社内でベースプログラムを共有して使い回すとか出来る。

  • 891◆jG/Re6aTC.23/04/28(金) 22:14:10

    さっきのテストプログラムでユーザーを差し替えて実行し動作確認...

    ヨシ!

  • 90二次元好きの匿名さん23/04/28(金) 22:14:43

    結構着々とやれてるのが見ていて気持ちいい

  • 911◆jG/Re6aTC.23/04/28(金) 22:44:32

    >>88

    はえー


    私はPython以外にも色々試すことが多いので自然とコンテナ中心の開発環境になりましたね



    >>90

    ありがとう!

    (でも裏ではちょくちょくコケてるよ)

  • 921◆jG/Re6aTC.23/04/28(金) 22:55:10

    あとついでにパスワード等をソースコードにベタ書きしているのが気持ち悪いので環境変数から取得できるようにする

    docker-compose.yamlのお隣に「.env」という名前のファイルを作成する
    中身はソースコードから接続情報を持ってくる

  • 931◆jG/Re6aTC.23/04/28(金) 23:00:09

    docker-compose.yamlに「env_file」セクションを追記し先ほど作成した.envファイルを指定するように記述

    後はコンテナ再起動

  • 941◆jG/Re6aTC.23/04/28(金) 23:03:26

    環境変数が大文字でないのが気持ち悪かったので修正

  • 951◆jG/Re6aTC.23/04/28(金) 23:09:58

    Pythonプログラムも調整

    os.environ を使って環境変数から値を取得する

    (import os を忘れずに)

  • 961◆jG/Re6aTC.23/04/28(金) 23:10:16

    実行して結果が変わらなければOK

    ヨシ!

    今は環境変数だけどk8sではconfigmapかsecretを使うことになりそう

  • 971◆jG/Re6aTC.23/04/28(金) 23:13:19

    キリがいいので今日はここまで

    明日はDBにテーブルを作成してデモデータ挿入、Python側で取得まで行ければいいな

    引き続きご意見ご感想質問アドバイスお待ちしております

    この時期だと就活生もいるかもだから「IT業界で働くってどう?」みたいなことも(守秘義務に違反しない範囲で)応えられたらと思います

  • 981◆jG/Re6aTC.23/04/28(金) 23:39:30

    >>79

    めちゃわかる

    エラー文は友達!コワクナイヨ


    むしろ一発成功する方が怖いまである

  • 991◆jG/Re6aTC.23/04/29(土) 04:59:35

    寝過ごす可能性があるので保守しとく

  • 100二次元好きの匿名さん23/04/29(土) 11:12:44

    import ~ as ~
    from ~ import ~ as ~

    asは「インポートしたもんを略称として~にする」という意味だよ、というクソ長いライブラリ名に対して便利なやつという豆知識。保守

  • 101二次元好きの匿名さん23/04/29(土) 13:42:18

    組み込み系で働いててWeb系の技術全然知らないので新鮮ですわぁ

  • 1021◆jG/Re6aTC.23/04/29(土) 14:50:11

    >>100

    asは良く使いますよね

    特にpandasとかpdで使われることが多い


    個人的には略しすぎるとこれ何だっけ?って思うことがたまにあるので余程じゃないと使わない派です


    メジャーな略称じゃないと他の人が読んだ時も混乱したりしますからね

  • 1031◆jG/Re6aTC.23/04/29(土) 15:12:01

    >>102

    あ、個人的なプログラミングではという話ですね


    お仕事でやるときはコーディング規約で決まってますからそれに従います

  • 1041◆jG/Re6aTC.23/04/29(土) 15:13:09

    >>101

    へえ組み込み系

    C系とかですかね?それともCOBOL?あるいはアセンブラ?まさかのbasic?

  • 1051◆jG/Re6aTC.23/04/29(土) 19:06:19

    外出から戻ってきたので続きをば

  • 1061◆jG/Re6aTC.23/04/29(土) 19:32:16

    むう、SQLAlchemy2.0でずいぶん記述方法が変わってるな

    ドキュメント読み込むまで時間かかるかも

  • 107二次元好きの匿名さん23/04/29(土) 19:57:49

    ORMあるからDB変わっても大丈夫理論…うっ…過去のトラウマが…
    いや、規模によるんだけどね、個人開発なら大丈夫だと思うんだけどね
    リポジトリパターンとかもそうだけど、本気で挿げ替え可能でしょって言ってくる人結構数いて辛い…DBとか一番依存関係の根っこにあるんだから無理だよ…

  • 10810123/04/29(土) 20:23:03

    >>104

    実機に乗せるものはCかC++

    内部の開発ツールとか自分で使うものはPythonかシェルスクリプトあたりですね

  • 1091◆jG/Re6aTC.23/04/29(土) 20:54:00

    >>107

    心中お察しします

    私もプロジェクトによってはパフォーマンス重視するからORM使わないとかいろんなケースがありましたね


    >>108

    別言語で書いてコンバートしてるんですね

    なるほど

  • 1101◆jG/Re6aTC.23/04/29(土) 21:06:20

    よし、簡単な部分は分かった

    次はテーブルを定義していく

    ここで言うテーブルとはデータの入れ物の型のこと

    テストとして「user」データを挿入するテーブルを作ってみる

    このようにテーブル定義をpyファイルで記述する

  • 1111◆jG/Re6aTC.23/04/29(土) 21:09:28

    __tablename__ は作成されるテーブル名

    id, name, fullname はそれぞれカラム定義

    idは数値でオートインクリメント(データ挿入時に自動的にカウントアップ)かつ主キーを設定している
    SQLAlcemyではテーブルに必ず1つは主キーが必要になる

    name, fullnameはそれぞれ文字列で定義

    これがテーブルの構成になりひいてはデータを取得した際の入れ物の型にもなる

  • 1121◆jG/Re6aTC.23/04/29(土) 21:12:28

    あとはメイン処理で利用するようimportする

    user.pyはmodelsディレクトリに置いたので

    from ディレクトリ名 import ファイル名(拡張子抜き) となる

  • 1131◆jG/Re6aTC.23/04/29(土) 21:14:34

    後はengine定義後にこのように記述

    これでテーブルが作られる...らしい

  • 1141◆jG/Re6aTC.23/04/29(土) 21:16:15

    あとは実行 ... あれ?

    エラー?

  • 1151◆jG/Re6aTC.23/04/29(土) 21:17:33

    エラー文を見ると

    str(文字列)であるuserにはBaseなんてないよ

    言われてるわけだが...

    userが文字列?

  • 1161◆jG/Re6aTC.23/04/29(土) 21:18:44

    あ!

    DB接続情報に使ってる変数名と被っとるやん!

  • 1171◆jG/Re6aTC.23/04/29(土) 21:20:36

    接続情報の方の変数名を一時的にusernameに変更

    (userテーブルはテスト用なので後で戻す)

  • 1181◆jG/Re6aTC.23/04/29(土) 21:21:51

    実行すると無事結果が返ってきた

    実行されたSQLも表示されている

  • 1191◆jG/Re6aTC.23/04/29(土) 21:23:49

    Webツールの方で確認してみてもきちんと作成できていることが確認できる

    ヤッタネェ!

  • 1201◆jG/Re6aTC.23/04/29(土) 21:25:23

    ちなみにこのcreate_all処理は作ろうとしているテーブルがすでに存在していた場合はスルーされるらしい

    なのでそのまま書いてても安心

  • 1211◆jG/Re6aTC.23/04/29(土) 21:27:15

    さて、ではこのテーブルにデータを追加していこう

    データの追加にはsession機能を使うためimport

  • 1221◆jG/Re6aTC.23/04/29(土) 21:31:47

    ついでにUserクラスの方もimport

    ...

    なんかこの辺汚いな

    本番ではちゃんと意識して命名しないと不味い気がするがとりあえずこのままで

  • 1231◆jG/Re6aTC.23/04/29(土) 21:36:55

    あとはセッションを定義

    セッション中に対象のデータをUserクラスを使って定義しsession.add_allで挿入対象に設定

    あとはsession.commit() で変更をコミットする

    という流れ

  • 1241◆jG/Re6aTC.23/04/29(土) 21:39:20

    さあ実行 ... が、またエラー

    さあ読み解きましょう

  • 1251◆jG/Re6aTC.23/04/29(土) 21:41:47

    うーん...

    挿入しようとした情報が足りてないのかな

    しかしidはautoincrementなので要らんと思うのだが

  • 1261◆jG/Re6aTC.23/04/29(土) 21:43:34

    物は試しでNoneを代入してみるか

    Noneは他でいうNull

  • 1271◆jG/Re6aTC.23/04/29(土) 21:45:26

    うーん駄目みたい

  • 1281◆jG/Re6aTC.23/04/29(土) 21:46:57

    あ!

    実行されたSQLよく見たら別にautoincrement反映されてないじゃん!

  • 1291◆jG/Re6aTC.23/04/29(土) 21:53:55

    どうやらIdentityクラスを使うのが正しいようだ

    それならそうと教えてくれてもいいじゃん!

    startで開始する値、incrementで増加する値を定義できるようだ

  • 1301◆jG/Re6aTC.23/04/29(土) 21:55:48

    テーブルを一度削除して再実行!

    お!行けたかな?

  • 1311◆jG/Re6aTC.23/04/29(土) 21:58:03

    Webツール側でデータを確認!

    ヨシ!

    みんなーー!すごいぞ!!SQLAlchemyでInsertできたぞ!!

  • 1321◆jG/Re6aTC.23/04/29(土) 23:47:30

    次は追加したデモを検索して取得してみよう

    必要なselectをimport

  • 1331◆jG/Re6aTC.23/04/29(土) 23:55:18

    insertのsessionはコメントアウトしてselect用のsessionを定義

    例としてfullnameが一致するデータを取得して表示するようにする

    SQLではなくてPython的に検索ロジックが記述できるのが嬉しい

  • 1341◆jG/Re6aTC.23/04/29(土) 23:56:41

    あとはプログラムを実行

    ヨシ!

    無事に一致したデータのみが出力されました!

  • 1351◆jG/Re6aTC.23/04/30(日) 00:02:32

    ちなみに全データを取得したければwhereの中に何も入れなければいい

    例では全データをID順に取得する

  • 1361◆jG/Re6aTC.23/04/30(日) 00:03:13

    このように全データが取得できる

  • 1371◆jG/Re6aTC.23/04/30(日) 00:04:47

    キリが良いので今日はここまでにします

    明日はアプリ用のDBを作ってそこにデモデータを準備

    さらにAPIと連携してDBのデータをアプリ画面に出すとこまで行きたいですね


    引き続きご意見ご感想ご要望アドバイスお待ちしております

  • 138二次元好きの匿名さん23/04/30(日) 00:11:22

    IT系に就職考えてるのでかなりありがたいスレ もっと早く気付きたかった

  • 1391◆jG/Re6aTC.23/04/30(日) 00:27:06

    >>138

    おお頑張ってください

    何か答えられることがあれば答えます


    GWはなんかアプリ作るにゃもってこいだそー

  • 1401◆jG/Re6aTC.23/04/30(日) 10:02:14

    保守

  • 141二次元好きの匿名さん23/04/30(日) 19:17:02

    スレ見ながら基本情報勉強してたけど落ちてしまった……

    再受験まで日があるのでスレ参考に自分も何かアプリ頑張って作ってみようかしら

  • 1421◆jG/Re6aTC.23/04/30(日) 19:33:31

    >>141

    見てくれてありがとう!

    そして残念だったね…


    個人的な意見だと基本情報とアプリ作成は結構畑が違うと思う(基本情報は情報世界のルール中心、アプリ作成はそのルール前提で動くもの中心)


    ただこれからちょっと先でやるつもりのアプリコンテナを動かす基盤…所謂インフラ構築は結構参考になるかも


    IPアドレスやhttps、ゾーニングなどののセキュリティとか実際に手で構築すると結構腑に落ちる


    何か答えられることがあれば力になりたいから質問は遠慮なくね

  • 1431◆jG/Re6aTC.23/04/30(日) 20:37:16

    出先から戻ったので続きやりまーす

    テーブルが作れることが分かったので次はデモデータに準じたテーブルを作るところからですね

  • 1441◆jG/Re6aTC.23/04/30(日) 21:13:43

    デモデータを基準にmodelをこんな風に書いていけばいい...のかな?
    まあ違ったり足りなかったりしたらその都度か次トライアルで直すつもりで

    デモデータから少し変えたところは

    - item_nameをnameに修正(テーブル名と重複するため)
    - バーコード値の格納先にcodeを定義
    - 入手先をsourceと定義
    - 最安値や最高値、平均値は蓄積したデータから算出する値なのでテーブルには定義しない

  • 1451◆jG/Re6aTC.23/04/30(日) 21:14:49

    class名がUserのままだったので修正

  • 1461◆jG/Re6aTC.23/04/30(日) 21:19:00

    modelのimport処理で怒られた

    Pytonのモジュール名にハイフンを使ってはいけないのをすっかり忘れていた

    ハイフンをアンダースコアに修正してimport

  • 1471◆jG/Re6aTC.23/04/30(日) 21:19:56

    あとはengineを定義してテーブル作成処理を走らせてみる

    上手くいくかな?

  • 1481◆jG/Re6aTC.23/04/30(日) 21:21:35

    うーんエラー

    まあいつものことです

  • 1491◆jG/Re6aTC.23/04/30(日) 21:23:22

    sourceの定義がおかしかったので修正

    というかこれエディタ側ではエラーにならんのか...

  • 1501◆jG/Re6aTC.23/04/30(日) 21:24:39

    お、コンソール出力上は成功したように見える

  • 1511◆jG/Re6aTC.23/04/30(日) 21:27:11

    Webツール側でも確認できました

    良かった これ でかいけつですね

  • 1521◆jG/Re6aTC.23/04/30(日) 21:32:26

    ただ実行されたSQLをよく見てみると各列はNOT NULL(空の値を許可しない)で定義されているんですね

    nameとかは良いとしてnote(備考)何かは空の値が入る可能性があるのでこれは困ります
    (""=空文字=Nullではない を入れても良いですが...どっちのが綺麗なんだろう)

    Modelの定義を変えましょう

  • 1531◆jG/Re6aTC.23/04/30(日) 21:36:09

    こんな感じでnullを許可したい要素にnullable=Trueを設定すると良いらしいです

    おそらくidとname以外はnullになる可能性があるはず...

  • 1541◆jG/Re6aTC.23/04/30(日) 21:38:40

    一度ITEMSテーブルを削除して再度テーブル作成を実行

    実行されたSQLにはnameしかnot null が指定されていないようなので成功みたいですね!

  • 1551◆jG/Re6aTC.23/04/30(日) 21:47:41

    よく考えたら賞味期限はDateTime(時分秒つき)じゃなくてDate(年月日のみ)でも良い気がしてきたな

    うーんでも午前中までとかいう指定もたまに見るな

    変えようと思ったけどそのままにしとこう

  • 1561◆jG/Re6aTC.23/04/30(日) 21:56:56

    sessionを定義してその中で追加するデモデータを定義、Insert、Commitするよう書いてみる

  • 1571◆jG/Re6aTC.23/04/30(日) 22:01:09

    実行!

    上手くいったっぽいかな?

  • 1581◆jG/Re6aTC.23/04/30(日) 22:03:38

    しっかり登録出来ていますね
    よき力だ...

  • 1591◆jG/Re6aTC.23/04/30(日) 22:04:56

    ホントは名称とかも外部キー参照にして正規化するのが一番いいんだろうけどとりあえずこのままでいいかな...

    パフォーマンスに課題が出た時に考えよう

  • 1601◆jG/Re6aTC.23/04/30(日) 23:10:21

    後は一応データを取得できるかも確認

  • 1611◆jG/Re6aTC.23/04/30(日) 23:10:58

    ちゃんと出力できてますね

  • 1621◆jG/Re6aTC.23/04/30(日) 23:41:12

    あとはこの処理をWebAPI(fastapi)側に持っていくだけですね

    しかしこれを既存構成にもっていこうとすると幾つかの解決しないといけない課題がありますね

    順に解決していきましょう

  • 1631◆jG/Re6aTC.23/04/30(日) 23:43:45

    まずは「もともとのFastAPIコンテナイメージにはoracledbやsqlalchemyモジュールは存在しない」という点

    さっきは手動でインストールしましたがFastAPIコンテナは本来自動起動なのでインストールする間もなく起動し、そしてモジュールがないのでエラーになってしまいます

    これを解決する方法はズバリ「必要なモジュールが既に入ったオリジナルコンテナイメージを作る」です

  • 1641◆jG/Re6aTC.23/04/30(日) 23:46:21

    まずはweb-apiディレクトリに「requirementes.txt」という名前のファイルを作りそこに事前にインストールしたいモジュール名を列挙していきます

  • 1651◆jG/Re6aTC.23/04/30(日) 23:48:20

    次にそのファイルのお隣に「Dockerfile」という名称で空ファイルを作成します

    これがオリジナルイメージのレシピ的ファイルになります

  • 1661◆jG/Re6aTC.23/04/30(日) 23:51:26

    最初の行に

    FROM もととなるイメージ:タグ

    を指定します

    今回はFastAPIのイメージをもとにしたいので「tiangolo/uvicorn-gunicorn-fastapi:latest」ですね

  • 1671◆jG/Re6aTC.23/04/30(日) 23:54:40

    次にさっき作った requirements.txt をイメージ側にコピーする記述をします

    COPY Dockerfileから見たファイルの相対パス イメージ側に配置したいファイルパス

  • 1681◆jG/Re6aTC.23/04/30(日) 23:57:21

    あとはrequirements.txtをもとにモジュールをインストールする処理を書きます

    RUN コマンド

    で指定のコマンドの実行結果を作成するイメージ側に反映できます

  • 1691◆jG/Re6aTC.23/05/01(月) 00:00:55

    よく見たらファイルのスペルミスっとる!
    英語苦手なのばれちゃう!!!!

    正しくは「requirements.txt」ですね...

  • 1701◆jG/Re6aTC.23/05/01(月) 00:03:08

    気を取り直してビルド実行

    成功するとこんな感じでコマンドで指定した「fridge-app/web-api:0.0.1」という名前でコンテナイメージが作成されます

  • 1711◆jG/Re6aTC.23/05/01(月) 00:04:44

    docker images コマンドでローカルに持っているイメージの一覧が表示されるのでここでも確認できますね

  • 1721◆jG/Re6aTC.23/05/01(月) 00:06:48

    後はdocker-compose側で参照するイメージをさっき作ったイメージに切り替えればOKです

  • 1731◆jG/Re6aTC.23/05/01(月) 00:09:59

    ついでにOracle接続情報が入った.envファイルも配置して参照するように追記しておきます

  • 1741◆jG/Re6aTC.23/05/01(月) 00:17:22

    次の課題は「型変換」です

    DBから取得したデータはItem型オブジェクトが入ってます

    しかしWebixにはJSON形式でデータを返す必要があるため変換する処理を考える必要がありますね

    web-apiのmain.pyのお隣に「utils.py」ファイルを作り変換用関数を作成します

  • 1751◆jG/Re6aTC.23/05/01(月) 00:20:32

    あとはmain.pyの方にテストで書いてたコードをお引越しします

  • 1761◆jG/Re6aTC.23/05/01(月) 00:22:37

    上の方でutils.pyをimportしていることに注意してくださいね

    あとは今までデモデータを返していた/fridge apiをDBから取得したデータを返すように変更します

    処理自体は凄いシンプルに書けますね

  • 1771◆jG/Re6aTC.23/05/01(月) 00:24:41

    あとはテーブル定義の際に変更した列名をdatasources.jsに反映させれば対応は完了のはずです

  • 1781◆jG/Re6aTC.23/05/01(月) 00:26:36

    ヨシ!

    ちゃんと値が取得できています!

  • 1791◆jG/Re6aTC.23/05/01(月) 00:27:21

    キリがいいので今日はここまで

    最安値や最高値など欠けている部分を処理するのはまた次ですね

    お疲れ様でしたー

    引き続きご意見ご感想ご質問アドバイスお待ちしております

  • 1801◆jG/Re6aTC.23/05/01(月) 12:18:00

    保守

  • 1811◆jG/Re6aTC.23/05/01(月) 19:06:41

    無料でWebアプリを作るRTA(リアル 楽しく あにまんプログラミング)はぁじまぁるよー

    早速再開です

  • 1821◆jG/Re6aTC.23/05/01(月) 21:12:58

    最安値とかを出したいのだけどその前に昨日書いた処理がもっといい感じに書ける気がしたので手を入れてみる

  • 1831◆jG/Re6aTC.23/05/01(月) 21:23:25

    こんな感じ

    pandasを使って検索結果をDataFrame形式で取得する
    pandasはデータ分析を支援するモジュールでPythonでは滅茶苦茶メジャー
    何ならpandasを使うためにPythonを使うまである

    DataFrame(以下df)はpandasをインポートすると使えるようになるオブジェクトでデータの入れ物

    この方が恐らくカテゴリごとの最大・最小・平均の算出もやりやすいはず

    dfはjson形式に変換することも簡単なので最終的な出力にも変換しやすい

    ちなみにppとはpprintでjsonなどのオブジェクトをコンソール出力するときに見やすく整形してくれるprintの拡張関数

  • 1841◆jG/Re6aTC.23/05/01(月) 21:25:17

    実行結果を見てみよう

    うーん狙い通りにはなってませんね?

    一つずつ解決してみます

  • 1851◆jG/Re6aTC.23/05/01(月) 21:28:04

    まず文字列が文字化けしてるように見えるのはasciiコードに変換されているみたいですね

    これはto_jsonの引数に force_ascii=False を指定すると解決するはず

  • 1861◆jG/Re6aTC.23/05/01(月) 21:30:48

    このままでもjsonといえばjsonですがwebixと連携する際はこのように「1つのjsonに全レコード(データ)が全て纏まっているjson」ではなく「1レコード1jsonが纏まった配列」にする必要があるためです

  • 187二次元好きの匿名さん23/05/01(月) 21:30:52

    実際、pandasは中身を理解していたら、データの取扱い周りの面倒なアルゴリズムをショートカット出来るから強いんですよね。良く解析でお世話になります。

  • 188二次元好きの匿名さん23/05/01(月) 21:33:13

    >>141

    基本情報

    午前:情報関連の基礎知識や計算知識、トレンドの問題

    午後:基本的なアルゴリズムを読み解く問題


    午前は基本的な知識を何度も反復。

    午後はアルゴリズム中心に今年度から変更になったのでアルゴリズムの流れ理解が必要。

    そんな具合だから基礎的なアルゴリズムを練習するのは良い勉強だよ。

  • 1891◆jG/Re6aTC.23/05/01(月) 21:35:12

    これにはto_jsonの引数に orient = 'records' を追加すればいいようです

    orient とは「合わせ方」みたいなニュアンスでそれを「records」形式にすることで1行1データにする形式に合わせてくれるようです

    今さらですがDBにおいて1行分のデータをレコードと呼びます

  • 1901◆jG/Re6aTC.23/05/01(月) 21:39:28

    無事jsonレコードの配列になってくれましたね

    さて残る課題はここ

    best_before_date(賞味期限)のvalueが数値になっています

    バグでしょうか?いいえ違います

  • 1911◆jG/Re6aTC.23/05/01(月) 21:42:25

    ここの値はdatetime型とマッピングされています

    datetime型は日付日時を保存できる型ですが「2023/05/01」のように文字列で持っているワケではなく数値で持っているんですね

    なのでこの数値をフォーマット(様式)を決めて文字列に変換してやる必要があります

  • 1921◆jG/Re6aTC.23/05/01(月) 22:35:59

    実際にはこんな感じですね

    json化する前にdf内のdatetimeを文字列に変換します

    strftime('%Y-%m-%d') で指定している'%Y-%m-%d'で2023-04-03 のようになります

  • 1931◆jG/Re6aTC.23/05/01(月) 22:38:00

    一括で指定列の全てのデータに対して処理できるのでやはりpandasを使うとこの辺りが楽ですね

    2023/04/03のようになってほしいからフォーマットをもう一回変えておきます

  • 1941◆jG/Re6aTC.23/05/01(月) 22:42:39

    よしよし

    きちんと変換されてますね

    スラッシュの前にバックスラッシュが入っているのはエスケープ処理(後ろの1文字を「ただの文字」として扱う処理)です

  • 1951◆jG/Re6aTC.23/05/01(月) 22:43:01

    キリがいいのでこのスレはここまでにしようと思います

    次スレを立ててきますね

  • 1961◆jG/Re6aTC.23/05/01(月) 22:47:56
  • 197二次元好きの匿名さん23/05/01(月) 22:49:39

    おつかれー!完走がんばれ!!
    I watch you!

  • 1981◆jG/Re6aTC.23/05/02(火) 03:26:51

    >>197

    ウレシイ…


    皆の反応が大きなモチベです

オススメ

このスレッドは過去ログ倉庫に格納されています