データベース使用(deta.space Collections Base編)

deta.space - collections データベース(Base)

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をクリックします。

collections選択

最初は空っぽなのでこのような画面がでます。「Get start」してください

base選択

そしてdriveを作るのかBaseを作るのか聞いてきます。今回はBaseです。

base選択

collection名を聞いてくるので「basesample」としました。

collection作成

続いてbaseの名前を入れます。今回は「my_items」です

collection作成

空っぽのbaseがでるのでまずは「add item」でキーを作成します。そしてエンピツのアイコンをクリック

base追加

itemの中身を追加して、「save item」します。

item追加

今入れたデータが保存されています

base追加

Baseをプログラムで作ってみよう

これでは、面倒なのでプログラムでつくることもできます。ただしその前にこのcollectionに対するdata keyを取得する必要があります。「collection settimgs」をクリックします。

data key取得

「create new data key」を押します

data key取得

「generate」をクリックします

data key取得

40桁位のdata keyが作成されて表示されます。これは2度と表示されないのでここで保存してください。あとで使います。

data key取得

普通に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を作ってもそのままでは表示されません。これでかなり迷いました。なんと表示するように追加しないといけない。この「+」をクリックします

data表示

そして「open exists base」を選び、候補にチェックを入れて「open selected」をクリックします。

data表示

編集や削除ができます。

Bases編集

クエリーを動かしてみる

baseのなかでクエリーをかけることができます。たとえばmy_itemsでidが1のものを探すには

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>
dir_myitems画面

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画面

relation_myitemsで実験できます

まだ正解がわかりません。もっとましな答えを探してみます