peeweeを使用したデータベースの操作

Peeweeについて

ORMフレームワークについて

ORMフレームワークについては以下の記事がわかりやすいです.

関係データベース(RDB)のレコードを、オブジェクトとして直感的に扱えるようにする。 また、RDBにアクセスするプログラムを書く際の煩雑な処理を軽減させ、 プログラマSQLを意識することなくプログラムを書ける。

https://qiita.com/yk-nakamura/items/acd071f16cda844579b9

model

from peewee import *
sqlite_db = SqliteDatabase('tutorial.sqlite3')


class BaseModel(Model):
    """A base model that will use our Sqlite database."""
    class Meta:
        database = sqlite_db


class Group(BaseModel):
    name = CharField()


class Member(BaseModel):
    name = CharField()
    group = ForeignKeyField(Group)

使い方

レコード保存

from peewee import *

sqlite_db = SqliteDatabase('tutorial.sqlite3')


class BaseModel(Model):
    """A base model that will use our Sqlite database."""

    class Meta:
        database = sqlite_db


class Group(BaseModel):
    name = CharField()


class Member(BaseModel):
    name = CharField()
    group = ForeignKeyField(Group)


sqlite_db.connect()
sqlite_db.create_tables([Member, Group])


with sqlite_db.atomic():
    for group_record in group_records:
        Group.create(name=group_record['name'])

# with sqlite_db.atomic():
#     for group_record in group_records:
#         Group.create(**group_record)

sqlite_db.close()

1対多(One-to-Many)のリレーションモデル

from peewee import *

sqlite_db = SqliteDatabase('tutorial.sqlite3')


class BaseModel(Model):
    """A base model that will use our Sqlite database."""

    class Meta:
        database = sqlite_db


class Group(BaseModel):
    name = CharField()


class Member(BaseModel):
    name = CharField()
    group = ForeignKeyField(Group, backref='members')


sqlite_db.connect()
sqlite_db.create_tables([Member, Group])

group_records = [{'name': "beatles"}, {"name": "bump"}]

with sqlite_db.atomic():
    for group_record in group_records:
        Group.create(name=group_record['name'])

beatles = Group.select().where(Group.name == 'beatles').first()
bump = Group.select().where(Group.name == 'bump').first()
member_records = [
    {'name': 'john', 'group_id': beatles},
    {'name': 'paul', 'group_id': beatles},
    {'name': 'fujiwara', 'group_id': bump},
    {'name': 'masukawa', 'group_id': bump}
]

with sqlite_db.atomic():
    for member_record in member_records:
        Member.create(**member_record)

# あるグループに所属しているメンバーが知りたい場合
group = Group.select().first()
for member in group.members:
    print(member.name)
# -----------------output-------------------
# john
# paul
# ------------------------------------------

# メンバーが所属しているグループが知りたい場合
member = Member.select().first()
for group in Group.select().where(Group.id == member):
    print(group.name)

# -----------------output-------------------
# beatles
# ------------------------------------------


# メンバーが所属しているグループが知りたい場合(INNER JOIN)
member = Member.select().first()
groups = Group.select().join(Member).where(Member.id == member.id)
for group in groups:
    print(group.name)
# -----------------output-------------------
# beatles
# ------------------------------------------

sqlite_db.close()

多対多(Many to Many)のリレーションモデル

from peewee import *

sqlite_db = SqliteDatabase('tutorial.sqlite3')


class BaseModel(Model):
    """A base model that will use our Sqlite database."""

    class Meta:
        database = sqlite_db


class Group(BaseModel):
    name = CharField()


class Member(BaseModel):
    name = CharField()
    group = ForeignKeyField(Group, backref='members')


class Office(BaseModel):
    name = CharField()


class OfficeGroup(BaseModel):
    group = ForeignKeyField(Group, backref='office_groups')
    office = ForeignKeyField(Office, backref='office_groups')


sqlite_db.connect()
sqlite_db.create_tables([Member, Group, Office, OfficeGroup])

group_records = [
    {'name': "beatles"},
    {"name": "bump"},
    {'name': 'amuro'}
]

with sqlite_db.atomic():
    for group_record in group_records:
        Group.create(name=group_record['name'])

beatles = Group.select().where(Group.name == 'beatles').first()
bump = Group.select().where(Group.name == 'bump').first()
member_records = [
    {'name': 'john', 'group_id': beatles},
    {'name': 'paul', 'group_id': beatles},
    {'name': 'fujiwara', 'group_id': bump},
    {'name': 'masukawa', 'group_id': bump}
]

with sqlite_db.atomic():
    for member_record in member_records:
        Member.create(**member_record)


office_records = [
    {'name': 'NEMS'},
    {'name': 'LONGFELLOW'},
    {'name': 'SONY'}
]

Office.insert_many(office_records).execute()


beatles = Group.select().where(Group.name == 'beatles').get()
bump = Group.select().where(Group.name == 'bump').get()

nems = Office.select().where(Office.name == 'NEMS').get()
long = Office.select().where(Office.name == 'LONGFELLOW').get()

office_group_records = [
    {'group_id': beatles, 'office_id': nems},
    {'group_id': beatles, 'office_id': long},
    {'group_id': bump, 'office_id': long},
]

OfficeGroup.insert_many(office_group_records).execute()


# beatlesが所属している事務所を知りたい場合
for office_group in beatles.office_groups:
    print(Office.select().where(Office.id == office_group.office_id).get().name)
# -----------------output-------------------
# NEMS
# LONGFELLOW
# ------------------------------------------

query = OfficeGroup.select(OfficeGroup, Group, Office).join(Group).switch(OfficeGroup).join(Office)

for i in query:
    print(i.group.name, i.office.name)

# -----------------output-------------------
# beatles NEMS
# beatles LONGFELLOW
# bump LONGFELLOW
# ------------------------------------------



sqlite_db.close()

javascriptのpromiseについて

概要

  • javascriptの非同期処理について調べると,Promise,Async,Awaitという単語が良く出てきます.この記事では,それらの違いを説明したいと考えています.

javascriptでの非同期処

callback

javascriptで非同期処理を行う手法としてcallbackがあります.textディレクトリにある,a,b,c,dのファイルを順番に読み込み,各ファイルに書かれている数字の和をコンソールに出力するプログラムを以下に書きます.

fs.readFile("text/a.txt", "utf-8", function(err, file) {
  //file = 1
  const a = parseInt(file);
  fs.readFile("text/b.txt", "utf-8", function(err, file) {
    //file = 2
    const b = parseInt(file);
    fs.readFile("text/c.txt", "utf-8", function(err, file) {
      //file = 3
      const c = parseInt(file);
      fs.readFile("text/d.txt", "utf-8", function(err, file) {
        //file = 4
        const d = parseInt(file);
        console.log(a + b + c + d);
      //  output: 10
      });
    });
  });
});

各変数のスコープが広いため多くの変数の中身を覚えないといけない点や,見た目が悪くコードを目で追にくい点など,可読性に問題点があります.

Promise

概要

上記で挙げた問題点や,そのほかの問題点を解決する手段としてPromiseが登場しました. callbackで書いた,各ファイルに書かれた数字の和を出力するプログラムをpromiseを使用して書くと以下のようになります.

const fs = require("fs");
const { promisify } = require("util");
const readFiles = path => {
  return new Promise(resolve => {
    fs.readFile(path, "utf-8", function(err, file) {
      const data = parseInt(file);
      resolve(data);
    });
  });
};

let sum = 0;
readFiles("text/a.txt")
  .then(value => {
    sum = sum + value;
    return readFiles("text/b.txt");
  })
  .then(value => {
    sum = sum + value;
    return readFiles("text/c.txt");
  })
  .then(value => {
    sum = sum + value;
    return readFiles("text/d.txt");
  })
  .then(value => {
    sum = sum + value;
    console.log(sum);
  });

他にもいろいろな書き方ができます.

const fs = require("fs");
const { promisify } = require("util");

const readFiles = path => {
  return new Promise(resolve => {
    fs.readFile(path, "utf-8", function(err, file) {
      const data = parseInt(file);
      resolve(data);
    });
  });
};
const a = readFiles("text/a.txt");
const b = readFiles("text/b.txt");
const c = readFiles("text/c.txt");
const d = readFiles("text/d.txt");

Promise.all([a, b, c, d]).then(function(files) {
    //files: [1,2,3,4]
    const sum = files.reduce(function (accumulator, currentValue) {
        return accumulator + currentValue
    });
    console.log(sum);
});

なんとなくすっきりしているのがわかると思います. promiseの効力がわかったところで,promiseの詳細を説明したいと思います.

promiseはmdnで以下のように説明されています.

Promise インターフェイスは作成時点では分からなくてもよい値へのプロキシです。 Promise を用いることで、非同期アクションの成功や失敗に対するハンドラーを関連付けることができます。これにより、非同期メソッドは、最終的な値を返すのではなく、未来のある時点で値を持つ Promise を返すことで、同期メソッドと同じように値を返すことができるようになります。

executor
2 つの引数 resolve と reject を取る関数。executor 関数は resolve 関数と reject 関数が渡されて Promise が実装されるとすぐに実行されます (Promise コンストラクターがオブジェクトを返すよりも前に executor は実行されます)。 resolve 関数と reject 関数は呼び出されると Promise に対してそれぞれ resolve (解決) もしくは reject (拒否) を行います。executor は通常、非同期の作業を開始して、完了した際に resolve 関数 (成功した場合) もしくは reject 関数 (エラーが発生した場合) のいずれか一方を呼び出します。 executor 関数でエラーが投げられた場合、 Promise は reject されます。 executor の返値は無視されます。 #### 構文 javascript new Promise( /* executor */ function(resolve, reject) { ... } ); 以下のプログラムでは,><で囲まれた箇所がexcutor関数になります. リクエストを送って,リクエストに対してのレスポンスの受信が成功したときに呼び出される関数,onload内でresolveを行い,エラー時に呼び出されるonerror内でrejectを行っています. javascript function myAsyncFunction(url) { return new Promise(/*>*/(resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url); //非同期処理の成功に対するハンドラ xhr.onload = () => resolve(xhr.responseText); // 失敗に対するハンドラ xhr.onerror = () => reject(xhr.statusText); xhr.send(); }/*<*/); }

promiseの持つ3つの状態

  1. pending:初期状態
    pendingはresolveが呼ばれることによりfulfilled状態,もしくはrejectが呼ばれることにより,rejected状態のいずれかに変わります.どちらになってもthenメソッドによって,状態によってのハンドラが呼ばれます.
  2. fulfilled: 処理が成功して完了した状態
  3. refected: 処理が失敗した状態

各状態への遷移を分かりやすく説明した図を以下に載せます.

f:id:igrrk:20181229155821p:plain
promiseの繊維

function asyncFunction() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve('Async Hello world');
        }, 16);
    });
}
asyncFunction().then(function (value) {
    return asyncFunction()
}).then(function(value){
    return value
}).then(function (value) {
    console.log(value)
}).catch(function (error) {
    console.error(error);
});

先ほどの図とプログラムの流れを照らし合わせながら説明すると 1. asyncFuntion()が呼ばれる

  1. promiseのインスタンスが生成され,setTimeoutが実行されます. この時,promiseの状態はpendingとなります.

  2. 16ミリ秒たつと'Async Hello world'という文字列がresolveされます.この時,promiseの状態はpendingからfulfilledに変わります.

  3. promiseの状態がfulfilledに変わると,thenメソッドが実行されます. 上記の例では,再びasyncFuntion()が実行され,新たにpromiseインスタンスが生成されます.promiseインスタンスの状態はpendingとなります.

  4. 16ミリ秒たつと'Async Hello world'という文字列がresolveされます.この時,promiseの状態はpendingからfulfilledに変わります.

  5. 4でのフェイズと同様にthenメソッドが実行され,valueがreturnされます.ここでわかるようにthenメソッド内でreturnすると,その値がpromiseオブジェクトとしてラップされ,returnされます.

  6. 最後に5でreturnされたpromiseオブジェクトをthenメソッド内で受け取り,コンソールに出力します.

このようにpromiseを使用することで非同期処理をチェーンのように書くことができます.チェーンのように書くことで,非同期をシンプルに書くことができ,結果として見通しの良いソースコードになります.

コマンドラインでDynamodbに存在する特定のテーブルのレコードを全て取得する方法

ローカルでdynamodbを起動

$ java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb

dynamodbに存在する全てのテーブルを取得

  • ローカルで動かす場合は,--endpoint-urlオプションにhttp://localhost:port番号を記述する(忘れがち)
$ aws dynamodb list-tables --endpoint-url http://localhost:8000

Dynamodb特定のテーブルのレコードを全て取得する

$ aws dynamodb scan --table-name テーブル名 --endpoint-url http://localhost:8000

pycharmとwsl(Windows Subsystem for Linux)の連携方法

wslとpycharmを連携させると嬉しいこと

  • wslはwindows上でネイティブにlinuxを動かすことができるため,コーディングの際にunix系osに依存したライブラリなどを使用できます. しかしwslの操作はcliで行うため,コーディングする際は必然的にvimなどを使用することになります.つまりideを使用することができないためコーディングの効率がかなり落ちます.そこで,wslとpycharmを連携させることで,windows上のideでコードを書きlinux上でコードを実行することができるようになります.

手順

  • wslで使用したいlinuxディストリビューションのインストールについては以下のサイトを参考にしてください.

    wslのインストール方法

    1. sshの設定を変更する

  • pycharmとlinuxを連携させるには,linux側でsshを立ち上げ,pycharmと接続を確立します. まず,pycharmからlinux側にsshで接続できるように,linux側のsshの設定ファイルを編集を行います. 以下の記述のように/etc/sshd_configを修正してください
PasswordAuthentication  yes
UsePrivilegeSeparation  no 

2.sshを起動する

  • linux側でsshを起動します.以下のコマンドをlinux側のterminalに入力してください
$sudo service ssh start
 * Starting OpenBSD Secure Shell server sshd
sshがちゃんと起動しているか以下のコマンドで確かめます.
$ ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root       130     1  0 00:01 ?        00:00:00 /usr/sbin/sshd
以上のようにsshdが起動していたら大丈夫です.

3.pycharmとlinuxを接続する

まずfile> setting > project interpreterに移動します.次に歯車のマークをクリックしてadd remoteをクリックします.すると以下のような画像が表示されます

f:id:igrrk:20180913001931j:plain

ここの画面ではsshで接続するための必要な情報を入力します.

  • hostの欄は,localhost
  • user名は,linuxのterminalに表示されているhost名
  • passwordは,linux側のpassword

となります. そして一番気をつけなればならないのは赤枠で囲まれているpython interpreter pathです. ここはlinux側でインストールしているpythonのパスを入力してください. 分からない場合はlinux側で以下のコマンドを入力し,出力されたpathを入力してください.

which python3
or
which python

これでpycharmとlinuxを接続することができました. しかしこの状態ではlinux上で毎回pythonコマンドを入力してpythonプログラムを実行しなければならず面倒です.そこでpycharmでlinux環境を使用してpythonプログラムを実行できるようにます.

4.pycharmでlinux環境でpythonプログラムを実行できるようにする

linux側で実行したいプログラムが置かれているディレクトリをpycharmで開き,remote interpreterを設定し,file > setting > project interpreterを開くと以下のような画面がでてくると思います. f:id:igrrk:20180913005058j:plain 緑いろの+を押して,

5.pycharmで実行したいファイルを設定する

そして,以下の画像の水色で囲まれている箇所をクリックし,編集すれば終わりです. f:id:igrrk:20180913010112j:plain