deta.spaceにデータベースを作る
deta.spaceはプログラムを作るだけでなく、データベースを作ることができます。Baseという名前がついています。
このBaseと他のデータベースとの違いは、NoSQLだということです。
NoSQLとは
今までの授業の中では、SQLの重要性を説き、いかにSQLを覚えるかに重点を置いてきました。ここでいうNoSQLとは、Not Only SQLの略でSQLを使わないでデータベースを扱うRDB以外のデータベースです。
処理速度、拡張性は優れていますが、検索が弱いといわれています。
RDBではリレーションを実行時に行いますが、NoSQLでは設計時に行うしかないようです。
Baseとはどんなデータベースか
BaseとはRDBではなく、ドキュメント指向型データベースで、要はJSONのようなものです。pythonの型の辞書型にランダムなKeyがくっついたようなものです。ドキュメント指向型DBで有名なのはMondoDBで世界で5番目にユーザーが多いようです。
テーブルをつくるには
deta.spaceはdeta.shと違って、テーブルやその中身を手動で作るGUIがあります。canvasからcollectionsをクリックします。
最初は空っぽなのでこのような画面がでます。「Get start」してください
そしてdriveを作るのかBaseを作るのか聞いてきます。今回はBaseです。
collection名を聞いてくるので「basesample」としました。
続いてbaseの名前を入れます。今回は「my_items」です
空っぽのbaseがでるのでまずは「add item」でキーを作成します。そしてエンピツのアイコンをクリック
itemの中身を追加して、「save item」します。
今入れたデータが保存されています
Baseをプログラムで作ってみよう
これでは、面倒なのでプログラムでつくることもできます。ただしその前にこのcollectionに対するdata keyを取得する必要があります。「collection settimgs」をクリックします。
「create new data key」を押します
「generate」をクリックします
40桁位のdata keyが作成されて表示されます。これは2度と表示されないのでここで保存してください。あとで使います。
普通にprojectを作ります。今回は「createbasetest」にしました。そのフォルダーを作って移動してからコマンドを入力します
space login space new
ここからプログラムを作成していきます。ただし、実行はできるのですが意味不明のエラーが出ます。
main.app
from deta import Deta deta = Deta("create keyで作成したbase key 40桁くらい") db1 = deta.Base("makers") db1.put({"id": 1, "name": "山田さん", "address": "鹿児島市中央町","tel": "099-123-4567"}) db1.put({"id": 2, "name": "斉藤さん", "address": "鹿児島市紫原2丁目","tel": "099-234-5678"}) db1.put({"id": 3, "name": "川上さん", "address": "鹿児島市吉野町","tel": "099-345-6789"}) db2 = deta.Base("my_items") db2.put({"id": 1, "maker_id": 1,"item_name": "いちご","price": 180,"keyword":["赤い","甘い","ケーキ"],"created":"2022-10-21"}) db2.put({"id": 2, "maker_id": 2,"item_name": "りんご","price": 90,"keyword":["丸い","赤い","パイ"],"created":"2022-10-22"}) db2.put({"id": 3, "maker_id": 1,"item_name": "バナナ","price": 120,"keyword":["パック","甘い","黄色"],"created":"2022-10-23"}) db2.put({"id": 4, "maker_id": 3,"item_name": "ブルーベリー","price": 200,"keyword":["袋入り","青い","眼精疲労"],"created":"2022-10-24"}) db3 = deta.Base("carts") db3.put({"id": 1, "item_id": 1, "count": 5}) db3.put({"id": 2, "item_id": 2, "count": 3}) db3.put({"id": 3, "item_id": 3, "count": 1}) db3.put({"id": 4, "item_id": 1, "count": 3}) db3.put({"id": 5, "item_id": 3, "count": 2}) db3.put({"id": 6, "item_id": 1, "count": 3})
さきほど作成したdata keyを Deta に与えてあげればオブジェクトが作成されます。このdata keyはgenerate作成時1回しか表示されないもので、あとからは表示できませんのでかならずMEMOしてください。
その作られたオブジェクトにobject.Base("Base名")で作成できます。そして作成したいデータを辞書型でputしてあげれば保存されます。
今回はphpの教科書「マイナビ よくわかるPHPの教科書」に出てくるサンプルのテーブルと同じものを作りました。ただkeywordのところはphpではカンマ区切りの文字列として保存していましたが、リストとして保存しました。このようにカラムの中に配列が保存できるのがNoSQLの面白いところです
requirements.txtにはdetaを入れます
requirements.txt
deta
SpaceFileも作ります
SpaceFile
# Spacefile Docs: https://go.deta.dev/docs/spacefile/v0 v: 0 micros: - name: createbasetest src: ./ engine: python3.9 primary: true public: true
実行方法は、まずはproject登録するので push
space push
canvasにprojectが登録されたら実行します。エラーが出ます。次が変なのですが、いくらbaseを作ってもそのままでは表示されません。これでかなり迷いました。なんと表示するように追加しないといけない。この「+」をクリックします
そして「open exists base」を選び、候補にチェックを入れて「open selected」をクリックします。
編集や削除ができます。
クエリーを動かしてみる
baseのなかでクエリーをかけることができます。たとえばmy_itemsでidが1のものを探すには
と入力して「run query」すると
のように絞り込まれます。クエリーを全部消せば、また全部出てきます。
いちごを探す item_name:"いちご" 甘いを含むものを探す "keyword?contains":"甘い" 2022-10-22より後の日 "created?gt";:"2022-10-22" 2022-10-22より後の日でかつ値段が180より小さいもの "created?gt":"2022-10-22","price?lt":180
このようにSQLのようなクエリーを実行することができます
Basesを利用したMicroの作成
Baseにはphpの教科書で使ったデータベースを入れてありますので、phpと同じように、まずは一覧表示を作ってみましょう。
mkdir dir_myitems cd dir_myitems space login space new
main.py
from flask import Flask, render_template from deta import Deta app = Flask(__name__) #リスト作成 # myitems = [{"id":1,"maker_id":1, "item_name":"いちご","price":180}] #データベース読込 deta = Deta("data key") myitems = deta.Base("my_items") @app.route('/') def index(): itemsdata = myitems.fetch(query=None) allitems = itemsdata.items allitems = sorted(allitems,key=lambda x: x['id']) return render_template('index.html', data = allitems)
/templates/index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous"> <title>商品一覧</title> </head> <body> <div class="container"> <h1 class="display-1">商品管理</h1> <table class="table table-striped"> <thead class="table-primary"> <tr> <th>ID</th> <th>メーカー</th> <th>商品名</th> <th>価格</th> </tr> </thead> <tbody> {% for a in data %} <tr> <td>{{ a.id }}</td> <td>{{ a.maker_id }}</td> <td>{{ a.item_name }}</td> <td>{{ a.price }}</td> </tr> {% endfor %} </tbody> </table> </div> </body> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script> </html>
projectをデプロイします
space push
これでデプロイ終了です。実行するにはcanvasに作成されたアイコンを選択してください。
これで動くは動きますが、メーカーの部分にメーカーIDが出てます。そうです、phpの時はMariaDBを使っていましたから、RDBだったわけで、リレーションDB用のテーブル設計になっています。
NoSQLでは、リレーションができませんので、このままでは不便です。対策としては、リレーションを使わずに直接メーカー名を入れて置く。またはそのたびにIDからメーカー名を取得して書き換える。あるいは、最初にテーブルを読み込んでおいて書き換える、などです。
今回は、メーカーが少ないので辞書型に読み込んで、書き換えるサンプルを作っておきますが、これが正しいとは思えません。
main.py
from flask import Flask, render_template from deta import Deta app = Flask(__name__) #リスト作成 # myitems = [{"id":1,"maker_id":1, "item_name":"いちご","price":180}] # makers = [{"id":1,"name":"山田さん","address":"鹿児島市中央町","tel":"099-123-4567"}] #データベース読込 deta = Deta("data key") myitems = deta.Base("my_items") #メーカーデータを展開する makers = deta.Base("makers") makerdata = makers.fetch(query=None) allmakers = makerdata.items mk = { m['id']:m['name'] for m in allmakers} @app.route('/') def index(): itemsdata = myitems.fetch(query=None) allitems = itemsdata.items allitems = sorted(allitems,key=lambda x: x['id']) for data in allitems: for key,value in data.items(): if key=="maker_id": data[key] = mk[value] #メーカーIDと名前をすり替える return render_template('index.html', data = allitems)
なんとか動きました
relation_myitemsで実験できます
まだ正解がわかりません。もっとましな答えを探してみます