MongoEngineつかってみた
はじめに
前回MongoDBをつかってみたという記事を載せました.その記事ではpyMongoを利用したMongoDBの操作を紹介しましたが,今日はPython上でMongoDBをOSMとして扱うことのできるMongoEngineの紹介をします.
MongoEngineインストール
インストールは簡単
pip install mongoengine
のみです.
使い方
学生情報を扱うデータベースを例に話を進めていきます. まずは,ドキュメントの追加と参照について説明します
追加と参照
データベース名:studentinfo
クラス名: Student(学生情報を扱う)
要素:Name(学生名)
要素:Number(学生番号)
import mongoengine mongoengine.connect("studentinfo") class Student(mongoengine.Document): Name = mongoengine.StringField() Number = mongoengine.IntField() #DBの初期化を行なう(ブログの内容には直接関係しない) Student.drop_collection() db=Student() db.Name="Taro" db.Number=1 db.save() for data in Student.objects: print data.Name print data.Number
出力だけならこれだけです.
もし特定のデータ(Number1にしましょう)だけ取りだしたいなら
import mongoengine mongoengine.connect("studentinfo") class Student(mongoengine.Document): Name = mongoengine.StringField() Number = mongoengine.IntField() #DBの初期化を行なう(ブログの内容には直接関係しない) Student.drop_collection() db=Student() db.Name="Taro" db.Number=1 db.save() for data in Student.objects(Number=1): print data.Name print data.Number
のようにすればNumber1のデータだけが表示されます
親子関係にあるデータベース
では次はMongoEngineで親子関係にあるデータベースを組んでみましょう. 例題として先程のStudentの加えて試験の結果を子とします.
データベース名:studentinfo
クラス名:Student
要素:Name(学生名)
要素:Number(学生番号)
クラス名:Math
- 要素:score(点数)
クラス名:Science
- 要素:score(点数)
クラス名:English
- 要素:score(点数)
とします.
この場合はStudentから各科目(Math,Science,English)を呼べるようにします.
import mongoengine mongoengine.connect("studentinfo") class Math(mongoengine.Document): score = mongoengine.IntField() class English(mongoengine.Document): score = mongoengine.IntField() class Student(mongoengine.Document): name = mongoengine.StringField() math = mongoengine.ReferenceField("Math") english = mongoengine.ReferenceField("English")
こうします.実際にStudentを経由してMathやEnglishのデータを書き込むには 次のようにします.ここでは[学生名taro,数学100点,英語50点]というデータと[学生名jiro,数学70点,英語80点]として書いていきます
taro = Student() math = Math() english = English() math.score=100 math.save() english.score=50 english.save() taro.name="taro" taro.math=math taro.english=english taro.save() jiro = Student() math = Math() english = English() math.score=70 math.save() english.score=80 english.save() jiro.name="jiro" jiro.math=math jiro.english=english jiro.save()
とします.mathとenglishそれぞれのインスタンスに各点数を代入したのちstudentのクラス変数に渡してますね.
値を取り出すには
for student in Student.objects: print student.name print student.math.score print student.english.score
のようにします. おそらく
taro 100 50 jiro 70 80
と出力されるはずです.
もし複数件入力されており,名前が"taro"だけのデータが欲しいときには
for student in Student.objects(name="taro"): print student.name print student.math.score print student.english.score
結果
taro 100 50
のようにobjectsにname="taro"のように指定するだけです.
ここの部分を一つのコードにまとめると
import mongoengine mongoengine.connect("studentinfo") #クラスの定義 class Math(mongoengine.Document): score = mongoengine.IntField() class English(mongoengine.Document): score = mongoengine.IntField() class Student(mongoengine.Document): name = mongoengine.StringField() math = mongoengine.ReferenceField("Math") english = mongoengine.ReferenceField("English") #DBの初期化を行なう(ブログの内容には直接関係しない) Student.drop_collection() #taroとjiroのドキュメントを追加 taro = Student() math = Math() english = English() math.score=100 math.save() english.score=50 english.save() taro.name="taro" taro.math=math taro.english=english taro.save() jiro = Student() math = Math() english = English() math.score=70 math.save() english.score=80 english.save() jiro.name="jiro" jiro.math=math jiro.english=english jiro.save() #Studentクラスに書かれているドキュメントをすべて取得 for student in Student.objects: print student.name print student.math.score print student.english.score #taroのドキュメントのみ取得 for student in Student.objects(name="taro"): print student.name print student.math.score print student.english.score
上書き
次に既存のデータを上書きしたときについてですが,上記のjiroをsaburoに変更してみましょう.
jiro = Student.objects(name="jiro") jiro.update(set__name="saburo")
のようにobjectsでデータを抜きだしてからupdate文で更新します.
更新の際にはset__のようにqueryを指定することができます.set__
query以外にもpush__
, unset__
, pull__
などがあります
詳しくはこちらをお読みください(http://mongoengine-odm.readthedocs.org/guide/querying.html) .
まとめ
MongoEngineでデータの追加や参照,更新についてまとめました.
MongoDBつかってみた
はじめに
Flaskやsinatraを使ってウェブアプリケーションを作成する際に,どのDBを採用するかというのは仕様決定上重要になります.
僕はこれまでDBというとMySQLやSQLiteのようなRDBMSを使っていたのですが,sinatra札幌さんのほうでflask上でNoSQLであるMongoDBを使用する機会を頂き,ここにMongoDBの概要と使い方,Pythonからどう制御するのかについて簡単にまとめました.
MongoDBとは
RDBMSを使用していた方にとってはMongoDBはちょっと異質に見えるかもしれません. RDBMSではデータ構造を明確化し,数学的な堅牢性を持っているイメージですね.SQLとよばれる言語を使用することでデータのソーティングなどを行ないます.
MongoDBではデータ構造はRDBほど明確化しなくても使用することができ,SQLのような言語を使う必要がありません.ドキュメント指向データベースと呼ばれており,データのやりとりはドキュメントとよばれる構造データにJSON形式で記述して使います.そのためプログラミング言語からSELECT * FROM テーブル名のようにコード中にSQLを書くこと必要がなく,使用言語がJSONフォーマットを扱えさえすれば配列やリストのような形でDBを操作できます.
MongoDBの用語
RDBMSを使用しているとデータベースの選択やレコードの選択などを考えてしまいますが,MongoDBは独特のドキュメント指向データベースと呼ばれている通りRDBMSとは異なる概念を持っています. ここではその概念と用語についてまとめてみました.
データベース:一つのアプリケーションで使用するコレクションを格納しておくもの.コレクションは複数持つことができる.
コレクション:ドキュメントを格納しておくもの.複数のドキュメントを持つことができる.
ドキュメント:商品ID,品名,価格などのデータそのものを格納できる.商品ID,品名のみなど可変長に扱える.入力形式はハッシュのようにKeyとValueの関係で保持される.
つまり包含関係で言えば「ドキュメント⊆コレクション⊆データベース」となります.
とりあえず動かしてみる
MongoDBを起動した状態で「mongodb」とシェル上からタイプするとMongoDBにアクセスできます.ここでは次のようなものを満すデータベースを作ってみましょう.
- データベース名:examDBという学校の試験を
*コレクション:Math, Englishの2教科を現わすコレクション
*ドキュメント:Name,Gradeの2項目.ただしGradeはテストを受けていない場合はつけない場合がある.
*要素:NameはTaro,Jiro, Saburoの3人.Taroは「Math40点,Englishは受けていない」. JIroは「Math100点,Englishは80点」.Saburoは「Mathは90点Englishは70点」.
データベースの生成や選択は
use examDB
としEnterを叩くだけです.簡単ですね 次にコレクション名とドキュメントを生成します.コレクションはMathとEnglishの2項目,ドキュメントはNameとGradeの2項目ですね.MongoDBではそれらを直接指定できます. まずはMathにそれぞれ項目を入力していきましょう.
db.Math.insert({Name : "Taro", Grade: "40"}) db.Math.insert({Name : "Jiro", Grade: "100"}) db.Math.insert({Name : "Saburo", Grade: "90"})
です. dbは上記のuseコマンドでexamDBをすでに選択しています.db.Mathとすることでコレクション名Mathを生成し操作できます.次にドキュメントを挿入したいのでdb.Math.insertのように指定できます.insertについては中身はJSONフォーマットで入力するだけなのでdb.Math.insert({ドキュメント項目名(Key) : ドキュメント項目値(Value), ・・・})です. 上記では3項目あるので3行分記述してます.
次にEnglishについても同様に打ちましょう.
db.English.insert({Name : "Taro"}) db.English.insert({Name : "Jiro", Grade: "80"}) db.English.insert({Name : "Saburo", Grade:"70"})
ですね.Taroは受けていないのでGradeの項目は入力してません.
では次は挿入されたデータを呼び出してみましょう. たとえば,Mathを呼び出したいときには MongoDBでは
db.Math.find()
だけです.これで入力した3項目が返ってきます.
MathのうちTaroのデータだけ呼びたいときには
db.Math.find(Name:"Taro")
だけです.
ここでは触れてませんが,コレクションの中にも子コレクションを生成することもできます. その場合もコレクション名が増えるだけで操作は,findやinsertを使用することができます.
PythonからMongoDBを操作する
PythonからMongoDBを操作してみましょう. PythonからMongoDBを扱えるライブラリにpymongoというのがあります. 「pip install pymongo」とタイプすることでインストールができると思います.
では先程の操作をpymongoからやってみましょう
from pymongo import MongoClient client = MongoClient()#接続先を指定Localhostの場合はvoidで良い db = client['examDB']#データベースを選択 #Mathの項目追加 db.Math.insert({'Name' : 'Taro', 'Grade':'40'}) db.Math.insert({'Name' : 'Jiro', 'Grade': '100'}) db.Math.insert({'Name' : 'Saburo', 'Grade': '90'}) #Englishの項目追加 db.English.insert({'Name' : 'Taro'}) db.English.insert({'Name' : 'Jiro', 'Grade': '80'}) db.English.insert({'Name' : 'Saburo', 'Grade':'70'}) #Mathのfind for data in db.Math.find(): print(data) #MathのTaroだけ呼びだす for data in db.Math.find({'Name':'Taro'}): print(data)
これらの形であればリストに格納したりイテレータのような形で扱うことができますね.
まとめ
MongoDBはRDBMSとは違ったSQLを使用しないデータベースであり,Jsonフォーマットでデータを扱えます.
概念用語としてデータベース,コレクション,ドキュメントがあります. ここでは学校のテストデータを例にmongoDBとPython上で試してみました.
kivyでデスクトップアプリケーション
はじめに
クロスプラットフォームでGUIアプリを開発するためのフレームワークにkivyがあります kivyのホームページ.
ここ最近,デスクトップアプリケーション開発といえば個人的にkivyを使用することが多くなりました.pythonにはpyQTやwxpython,Tkinterなど多くありますが,pyQTはウェブや書籍を参考にできるほど文献が少ないこと,wxpythonの場合は更新がすでに止まっており,採用には至りませんでした.Tkinterを使うことも考えられますが,kivyのほうがスマートフォン上で動作するという違いがあり興味が湧いたのでkivyを使うに至りました.
ここではkivyの基本構成について忘れないようにメモしておきます.
必要なリファレンスについては
と
PythonでかんたんiOSアプリプログラミング―Kivyによるマルチタッチアプリケーション制作
を参考にしました.
kivyのインストール
少し前のブログをお読みください kivyをpipからインストールしてみた
本記事ではPythonは2.7.6,kivyのバージョンは1.8を対象に書いております.
kivyの開発
まずはHelloWorld
kivyには様々なウィジェットがあります.そのなかにもラベルと呼ばれるアプリケーション上に文字列を表現するものがあります.ここではラベル上に「HelloWorld」を出力するコードを示します.
from kivy.app import App from kivy.uix.label import Label class TestApp(App): def build(self): return Label(text='Hello World') TestApp().run()
これだけですね.
from kivy.app import Appについてはアプリのインポート,二行目はラベルのインポートです.
公式サイトのhomepageにも載っていますが,labelとLabelの箇所をそれぞれbutton,Buttonに変更すればボタン上にHelloWorldが表示されます.
pythonでファイルを指定すれば実行できます.
ちょっとまともなGUIアプリ
さて,HelloWorldの例を読むだけではreturnでボタンを返すだけでは面白くないかもしれません. ここでは僕が普段利用しているファイル関係を示しておこうと思います.
以下に基本的なファイル関係を示しておきます.
├── main.py ├── my.kv
main.pyという実行ファイル,my.kvというGUIレイアウトなどを記述するファイルです. メインとなる実行ファイルについてはHelloWorldを表示したようにPythonで処理を記述するものです.
my.kvはkivyにおけるデザイナの位置やIDと呼ばれるGUI部品とコードを紐付けるものを定義するものです.pythonコードで部品を設置しなくてもmy.kvに部品を配置することで,main.pyでは見た目を気にせず処理内容にだけ注力することができます.
また,名前を変更することも可能ですが,初期設定ではmy.kvという名前のファイルを読むように設定されており,そこについてはここでは触れません.
以下に,ボタンを押したらラベルの値に現在時刻を表示するアプリを例にmain.pyとmy.kvの設定や関係を説明していきます..
まずはmy.kvの説明をします.
今回作るアプリはボタンを押したらLabel上に現在時刻を表示するものです. そのため,ウィジェット名とラベル,ボタンをそれぞれ一個づつ配置する必要がありますので配置していきましょう.
まずは,ウィジェット名と任意の名前をラベル・ボタンそれぞれに付けていきましょう.
ここでは
ウィジェット名はtimerWidget
ラベルの名前はlabeldatetime
ボタンの名前はbuttoncount
としました.
それぞれの部品設定を設定を以下に示します.
labeldatetimeの設定
フォントサイズ:50pt
位置(横,縦):(中央,アプリ上部)
初期設定の文字列:「time」
buttoncountの設定
*ID: buttoncountval
*フォントサイズ:50pt
*位置(横,縦):(中央,アプリ上部から下方向に向かって180ピクセル)
*ボタンサイズ(幅,高さ):(140,70)
*設定の文字列:「Click!」
これをkvファイルで表現すると次のようになります.
my.kv
#:kivy 1.8.0 <timerWidget>: labeldatetime: labeldatetimeval buttoncount: buttoncountval #Labeldatetime Label: id: labeldatetimeval font_size: 50 center_x: (root.width/2) top: root.top text: 'time' #buttoncount Button: id: buttoncountval font_size: 50 center_x: (root.width/2) size: 140,70 top: root.top-180 text: 'Click!'
まず一番上で,kivyのバージョンとウィジェット名,部品IDとpythonコードで使用する部品名を紐付けています. あとはそれぞれフォントサイズや位置,部品サイズ,部品の文字列などを定義しているだけです.
次にmain.pyで処理を書いていきましょう.
ここではウィジェットとして定義した部品を操作する箇所です.ボタンが押されたらその都度表示時刻を現在時刻に置きかえるというものです.
main.py
# -*- coding: utf-8 -*- import kivy kivy.require('1.8.0') from kivy.app import App from kivy.uix.widget import Widget from kivy.properties import ObjectProperty import datetime class timerWidget(Widget): labeldatetime = ObjectProperty(None) buttoncount = ObjectProperty(None) class MyApp(App): def buttoncount_clicked(self, src): self.root.labeldatetime.text = str(datetime.datetime.now()) def build(self): self.root = timerWidget() self.root.buttoncount.bind(on_press=self.buttoncount_clicked) return self.root if __name__ == '__main__': MyApp().run()
上から説明するとまずは,kivyのバージョン,アプリ起動に必要なAppとWidget,kvファイルで定義したWidgetを取りだすObjectProperty,現在時刻を扱うdatetimeをインポートしています.
class timerWidget(Widget)ではmy.kvで紐付けられている部品を取りだしたり描画する箇所です. my.kvで定義した変数を左辺に置いて右辺にはObjectProperty(None)と記述するだけです.
class MyApp(App)では起動に必要な準備やイベントハンドラと呼ばれるボタンをクリックしたときの処理などを書いていきます.
timerWidgetを生成. つぎにボタンをクリックしたときの処理をbind(on_press= )で定義しています. 最後にウィジェット自体を返すだけです.
次にMyApp().run()で実行です
以下に実行画面を示します
実行画面(クリック時)
まとめ
簡単なkivyの使い方をまとめてみました. 必要な部品などの配置はkvファイルが担当し,部品を操作するのはmain.pyです.
kivyにはもちろんこれ以外にもたくさんの部品があります.
また形には見えない部品もあります.例を挙げれば一定の時間で自動で実行するClockなどです.機会があればその辺についても書きたいと思います.
kivyをpipからインストールしてみた[2014/9再編集]
2014年9月現在一部インストール方法ではうまく導入できなかったので少し修正しました.
はじめに
Pythonにはkivyというクロスプラットフォームで動作するPython用のGUI開発ライブラリがあります。 それを使用することで、MacOSX,Linux,Windowsの3つのプラットフォームで動くほか、iOSやAndroid上でも動作するアプリも開発ができます。
今日はこのインストールで少し躓いたのでpyenvという様々なバージョンを導入できるツールからkivyまでの導入をメモとして残しておきます。
環境はUbuntu13.04 32bit、シェルはzshです。 pyenvでは「2.7.6」をインストールするものとします。
pyenv
pyenvのインストールと2.7.6をインストールする例を交えながら使い方を説明します。
pyenvのインストール
まずは、apt-get(またはaptitude)でpyenvのための必要パッケージを導入していきます。
sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev
そのあとは、https://github.com/yyuu/pyenv#installationに従ってインストールするだけです。
cd git clone git://github.com/yyuu/pyenv.git .pyenv echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zprofile echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zprofile echo 'eval "$(pyenv init -)"' >> ~/.zprofile exec $SHELL
ですね。
pyenvの使い方
こちらもさきほどのページの通りです(https://github.com/yyuu/pyenv#command-reference)
メモしておきますと
インストール可能なバージョン一覧
pyenv install -l
任意のバージョンのPythonをインストール(ここでは2.7.6)
pyenv install 2.7.6
任意のバージョンに切り替える(グローバルな設定)
pyenv global 2.7.6 pyenv rehash
これでpython2.7.6を使用できるようになりました。
kivy
pyenvでインストールしたPythonにはpipが内蔵されています。 今回はこのpipを使ってkivyのインストールを行ないます。 こちらに書いてあるインストール手順を踏んで行ないました。 http://kivy.org/docs/guide2/basic.html
インストール手順ページにある通り、kivyはcythonが必要なのでインストールし、その後はなぜかpygameをインストールしてから、kivyをインストールするという順のようですね(ここのページに辿りつくのに苦労しました・・・)。 この記事でもこの流れに沿って書いていきます。
cythonのインストール
cythonは次のコマンドで一発だと思います。
pip install cython
pygameのインストール
ここのURLを参照して必要パッケージを導入しました。 http://stackoverflow.com/questions/7652385/where-can-i-find-and-install-the-dependencies-for-pygame
sudo apt-get install python-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libsdl-ttf2.0-dev libsdl1.2-dev libsmpeg-dev python-numpy subversion libportmidi-dev ffmpeg libswscale-dev libavformat-dev libavcodec-dev
これにさらにfreetypeのconfigがうまくいってない旨のエラーがでましたので、
sudo apt-get install freetype6-dev
で追加しておきました。
あとはpygameをmercurialのリポジトリを経由してpipでインストールします。
pip install hg+http://bitbucket.org/pygame/pygame
kivyのインストール
いよいよkivy本体のインストールですね
pip install git+https://github.com/kivy/kivy.git
でインストールが成功すれば終わりです。
pip install kivy
では2014年9月現在ではコケてしまいましたので,
githubのmasterブランチから直接インストールしてあげるという方法を取りました.
まとめ
pyenvからkivyまでをインストールしました。
pyenvで2.7.6をインストールしたあとはpipを使ってcython,pygameという順にインストールしていき、最後にkivyをインストールしました。
pyenvやpygameをインストールするときには必要パッケージをaptから取り込んでおく必要があります。また、zshやbashを使う環境によって設定を記述するファイルを分ける必要があります。今回はzshで導入を行ないました。
Sinatraでグラフ画像を表示
SinatraAdventCalendar11日目の記事です(http://www.adventar.org/calendars/262)
昨日はRSSFeed(http://advent.nzwsch.com/rss-feed)でした.
今回は,sinatraで遊んでみたような内容です.
力を抜いて読んでください.
はじめに
今年の初頭あたりにipodに入れていた歩数計のデータをウェブブラウザ上からグラフ表示してみたのですが,当時,僕はフレームワークを使ったことがありませんでした.内容としてはpythonからipodに入っていた歩数計データを読みだして,matplotlibでグラフ生成したあと,htmlに読み込ませるというものです.
フレームワークを使えばもっと素早く簡単にできるだろうと考えて,今回RubyのSinatraでウェブ上にグラフ画像を表示してみました.
内容は同じでは面白みがないので,東京電力の提供している電力使用量をグラフ化するコードにしました. 一年間の温度変化などでも良かったのですが,更新頻度の高いデータのほうが動的なアプリケーションとして面白いと判断したため このような題材を選びました.
他には二酸化炭素などの公害の原因となる物質の量をグラフ化してみようと考えましたが,きっちりとWebAPI化が進んでいるようで, 面白みが無いと判断して今回はやめました.
処理の内容
ユーザが訪れるたびにcsvファイルをダウンロードしてGruffというライブラリでグラフ画像を生成し,ウェブ上に表示するというものです.
一応,csvダウンロード先のウェブサイトへの負担を考えて,5分立たなければダウンロードできない仕組みとしてます.
図:処理のフローチャート
各ファイルの構成は次のようになっています
. ├── grufftest.rb ├── juyo-j.csv ├── public │ └── PowerUsageGraph.png └── views ├── index.erb └── layout.erb
必要なライブラリの解説
まずはsinatraですね.こちらは必須です.
gem install sinatra
でインストールが可能です.
今回はgruffというRubyで見た目重視のグラフライブラリを使用してみました.
matplotlibはGnuPlotのように見やすいのですが,飾り気がないので面白みがありません(グラフに本来求める要素ではないですからね).
こちらのライブラリはgem install gruff
でインストールできます.
gruffの使い方は簡単で,公式サイト(http://nubyonrails.com/pages/gruff)に沿えばすぐに使い方がわかると思います.
以下にサンプルのコードを示します
require 'rubygems' require 'gruff' g = Gruff::Line.new g.title = "My Graph" g.data("Apples", [1, 2, 3, 4, 4, 3]) g.data("Oranges", [4, 8, 7, 9, 8, 9]) g.data("Watermelon", [2, 3, 1, 5, 6, 8]) g.data("Peaches", [9, 9, 10, 8, 7, 9]) g.labels = {0 => '2003', 2 => '2004', 4 => '2005'} g.write('my_fruity_graph.png')
g.dataには項目名と数値リテラルの配列を渡し,g.labelsにはハッシュで横軸の項目を入れているんですね. そこまで整ったらg.writeで保存したいグラフ画像名を入れて保存です.
コード
ではさっそくコードを載せます.
main.rb
require 'gruff' require 'csv' require 'sinatra' require 'date' def graphgenerate(y,x_name,y_name,graphname) g = Gruff::Line.new g.title = graphname g.data(graphname,y) g.labels = {0 => '0',12 => '1',24 => '2',36 => '3',48 => '4',60 => '5',72 => '6',84 => '7',96 => '8',108 => '9',120 => '10',132 => '11',144 => '12',156 => '13',168 => '14',180 => '15',192 => '16',204 => '17',216 => '18',228 => '19',240 => '20',252 => '21',264 => '22',276 => '23'} g.x_axis_label = x_name g.y_axis_label = y_name g.write('./public/'+graphname) end def filedownload(url,filename) system('wget '+url+' -O'+ filename) system('nkf -w --overwrite '+ filename) end def fileanalysis filedownload('http://www.tepco.co.jp/forecast/html/images/juyo-j.csv','juyo-j.csv') powerdata=[] count = 0 datatime="" CSV.foreach("./juyo-j.csv","r") do |data| if(count==0) then datatime=data[0] end if(count>46) then powerdata << data[2].to_f*10000/10**6#kWはわかりにくいのでMW end count=count+1 end graphgenerate(powerdata,'Hour','Power Usage[MW]','PowerUsageGraph.png') return datatime end get '/' do if((DateTime.now.to_time-File.stat("./juyo-j.csv").mtime.to_time).to_i > 300) then @graphdatetime=fileanalysis end @graphimage="PowerUsageGraph.png" erb :index end
index.erb
<h1>本日の電力使用状況(東京電力)</h1> <p>参照:http://www.tepco.co.jp/forecast/html/images/juyo-j.csv</p> <img src=<%= @graphimage %>> <h2><%= @graphdatetime %></h2>
layout.erb
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>本日の電力使用量</title> </head> <body> <%= yield %> </body> </html>
簡単な解説
次のような3つのメソッドの呼び出しで動作を実現しています.
・graphgenerate(y,x_name,y_name,graphname)
・filedownload(url,filename)
・fileanalysis
・graphgenerateですが,こちらでグラフ画像を生成しています. サンプルにあるものをいじっただけです.
・filedownloadはwgetコマンドをrubyの上から実行し,ダウンロードしてきたcsvファイルをutf-8に変換しています. rubyの上でも実現できるかもしれませんが,てっとり早い方法なのでこのような処理をしています.
・fileanalysisは上二つのメソッドをまとめたものです. get '/' do以下のメソッドで記述しても良いですが,長くなったのでこのようなメソッドにまとめました. CSVを読み込んでいるところではcountが0であるときと,47以上のときに実行されるif文があります.これは更新日時が1行目にあり,グラフ化したいデータが47行目にあるためです.Rubyではこのあたりもっと簡潔に書けそうですが何かないでしょうか?
なお,生成した画像はpublicディレクトリに移さなければerb側で表示ができませんのでおいておきました.
erbの配置については,前のブログに書いたとおりです.よろしければ参考にしてください (http://kuroneko0208.hatenablog.com/entries/2013/11/26).
表示結果
localhost:4567にアクセスすると次のように表示されます.
まとめ
sinatraを使って東京電力の提供している電力使用量をグラフを表示してみました.
グラフ生成にはGruffというビジュアル重視のグラフ生成ライブラリを使用することで,グラフ画像の生成を行ないます.
グラフ画像はpublicにおかなければ反映されないのでおく必要があります.
フレームワーク素人がFlask触ってみた
こんにちは
昨夜に引き続きブログを更新してみます. 前回の内容はこちら(http://kuroneko0208.hatenablog.com/entry/2013/11/26/034323)
今日は,昨晩更新したSinatraと同じ内容をPythonのFlaskで書きなおしてみました.
昨日と同じ流れで進めていきますね.
まずはHelloWorld
#coding:utf-8 from flask import Flask app = Flask(__name__) @app.route("/") def index(): return "helloworld" app.run()
flaskを利用するため最初の行で呼びだしてきております. 次に@という形でデコレータを表現しております. Sinatraとは若干書きかたが違うもののルートを示していることはおそらくSinatraをお使いのかたはわかるかと思います. Rubyの場合はreturnをつけなくても最後に記述した内容がreturnされるらしいのですが, Pythonにはそのような機能は無いはずですので(?)明示的に示しております. app.run()で実行を示しております.
ちゃんとしたhtmlページに表示させたい
昨夜見せた内容をPythonでディレクトリを構成すると次のようになります.
. ├── main.py └── templates ├── index.html └── layout.html
実はPythonではMVCモデルと呼ばずにMVTモデルと言います. MVCでいうViewにあたるものがPythonではTemplatesなのです. ちょっとややこしいのですが,このようになっております. layout.htmlやindex.htmlはsinatra同様,共通部品を集めたものをlayout.htmlへ index.htmlが呼びだされたときだけに基本出力したいときにはindex.htmlへ記述します.
以下にmain.pyがlayout.htmlとindex.htmlを利用してウェブアプリケーションを立ち上げるコードを示します.
main.py
#coding:utf-8 from flask import Flask, render_template app = Flask(__name__) @app.route('/') def index(): return render_template('index.html',titlename="helloworld") app.run()
layout.html
<!DOCTYPE html> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>{{ title }}</title> </head> {% block body %} {% endblock %} </html>
index.html
{% extends "layout.html" %} {% block body %} <h2> テスト </h2> {% endblock %}
まずmain.pyから説明していきますと,importやindex関数の中にreturn render_template('index.html',title="helloworld")
があるかと思います.
こちらの関数でtemplateエンジンのhtmlをレンダリングするように命令しています.
今回呼びたいのがindex.htmlで,タイトルが"helloworld"としたのでこのようになりました.
では次はlayout.htmlについて見ていきます.
こちらは{{ title }}
と{% blockbody %}'から'{% endblock %}
があるかと思います.
こちらがテンプレートエンジンの書き方になっておりまして,{{ なにかの変数 }}という形で書くことでmain.pyで渡した変数を呼びだせます.
{% blockbody %}
と{% endblock %}
についてはsinatraでいうところのyieldと言ったところです.ようするにmain.pyの書き方ではindex.htmlが呼ばれておりますから,この場合index.htmlの内容が出力されます.
index.htmlについて見ていきましょう.
{% extends "layout.html" %}で共通化部品(親のこと)を指定しています.
{% block body %}
と{% endblock %}
で囲ったところが呼び出されるようになっておりますから,main.pyを実行するとh2要素タグのサイズの「テスト」という文字列が画面に出てきます.
htmlからテキストボックスの内容をPythonに送信
次に昨日もやったテキストボックスの内容をpostでpythonに渡して処理させるコードを書きます. 昨日と同様にタイトルを逐一書きかえるというものとしました.
では以下コード main.py
#coding:utf-8 from flask import Flask, render_template,request app = Flask(__name__) @app.route('/') def index(): return render_template('index.html',title="helloworld") @app.route('/sendtext', methods=['POST']) def sendtext(): return render_template('index.html',title=request.form['message']) app.run()
index.html
{% extends "layout.html" %} {% block body %} <h2> テスト </h2> <form method="post" action="/sendtext"> <input type="text" name="message"><input type="submit" value="Send"> </form> {% endblock %}
layout.html
先程と同様なので省略
main.pyから見ていきますと,postで受けとるのに@app.route('/sendtext', methods=['POST'])
としていますね.実は第二引数としてmethods=['POST']
を書けばポストの処理となります.
それをrequest.form['message']
という形でindex.htmlから送られてくるだろう'message'を受けとってtitleに代入して再びレンダリングしています.
一応最後に,index.htmlを見ますと通常のhtmlらしく"message"というテキストボックスとSendというボタンがあります.テキストボックスで書いた内容を/sendtextに向けてpostで送信するというものとなっております.
少し感想
RubyのSinatraとPythonのFlaskの二つを試した感想として,Sinatraのほうがウェブ上での文献の数が非常に多く,Rubyをお使いのかたはFlaskを触る利点があまりないかもしれません.僕自身もPythonばかり使ってきましたが,Sinatraの文献の多さやとっつきやすさで2日に分けた記事を書くことができました.
Flaskの場合はまだまだ日本語記事が少ないことなどがありまして,調べにくいのですが,機能面で言えば,基本的にはSinatraに見劣りしないくらいのフレームワークだと思います.Pythonを書いてきた人にとっては重要なリソースもあるでしょうから,わざわざSinatraを触る必要がないかもしれません.ただ,僕のようにフレームワーク初心者にとっては,Sinatraの文献の数に助けられ,結果的にFlaskで簡単なアプリケーションを作るまでいけましたので,Sinatraもおすすめです.
そのようなわけで結論として,Ruby使いはFlaskを使う利点が見あたらないが,Python使いにとってはどちらも学んだほうが良いと言ったところでしょうか.
フレームワーク素人がRubyのSinatra触ってみた
こんにちは
最近,Python以外にもちょっとしたきっかけでRubyのSinatraに手を出しました.
僕自身もともとウェブフレームワークに触れることを今までしなかったことから,あの独特の開発のしかたに妙な苦手意識を持ってました.
ここでは似たような境遇にある?かたへ少し参考になればと思い残しておきます.
まずはハローワールド
main.rb
require 'sinatra' get '/' do "HelloWorld" end
まずはrequireでsinatraを呼んできます.
そのあとにget '/' do
というsinatra流の文があります.
これはgetメソッド(普通にURLでアクセスした場合のこと)と'/'というlocalhost直下を意味する文からなりたってます.
この場合意味は「普通にURLでlocalhostを叩いた場合に実行」と読むことができます
で,"HelloWorld"と書いてあるのが文字列となってまして,これが表示されます.
ちゃんとしたhtmlページに表示させたい
今のような書きかたでは"hello world"を出すだけであまりできそうなことがありません. 通常はhtml(例えばindex)を意味するファイルを生成してひっぱってきて表示するのが基本です.
Sinatraの場合,erbという形式でhtmlを表現することになります(他にもHAMLというものがありますが,ここではerbにしておきます). 以下にこれらの基本構成を表示しておきます.
. ├── main.rb └── views ├── index.erb └── layout.erb
main.rbに先程のような呼び出されたアクションを記述して views以下にその結果を垂れ流すイメージですね.
views以下を見るとindex.erbとlayout.erbの二つがあると思います. これはテンプレートエンジンの特徴なのですが,基本的な共通するコードをlayoutに書いておいて,index.erbにはindexが呼び出されたときの処理を書くというやりかたです. 例えばabout.erbのようにaboutページを生成された場合ヘッダーの部分やtitleの部分,cssの部分などはindexと同じようにしておきたいというケースがあるかと思いますが,そのような場合に有効です.
以下にそれっぽく動くmain.rb,index.erb,layout.erbを記述しておきます.
main.rb
require 'sinatra' get '/' do @title = "ハローワールド" erb :index end
layout.erb
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title><%= @title %></title> </head> <body> <%= yield %> </body> </html>
index.erb
<h2>テスト</h2>
このように書くだけです.
まずmain.rbの@title = "ハローワールド"
はindexにtitleという"ハローワールド"が代入された変数をindex.erbに渡すための書きかたです.@変数名としておくことこのような機能が使えます.
erb :index
というのはindex.erbを呼びだすときの文です.
たとえばhamlで書きたいときにはhaml :index
という形式になります.
次に,layout.erbですが,これはhtmlの基本的な骨組だけを書いておく部分です.
<!DOCTYPE html>
は必ずどのページにも登場しますね?そういった決まりきった共通項を書いておく部分です.このようにlayout.erbに書いておくと勝手にsinatraが解釈してくれます.
それからlayout.erbの中には<%= @title %>
や<%= yield %>
とありますね.
<%= @title %>
の場合はrbファイルで書かれた@titleが渡された内容を出力していることになります.
<%= yield %>
のほうは,indexページが呼ばれたときには「index.erbの中身を展開しなさい」という意味になります
<%= なんとか %>
という書きかたに違和感があると思いますが,こちらはerb流の書きかたになっております.
つぎにindex.erbはといえば<h2>テスト</h2>
ですね
つまりルートにアクセスした場合ページにはテストとh2のサイズで表示されます.
erbからテキストボックスの内容をrubyに送信
今はただ単純にウェブページに記述しておくだけでした. ここではユーザの入力に応じた処理としてテキストボックスの内容をボタンが押されたらrbファイルにpost形式で送る方法を書いておきます.例としてタイトルを逐一変更するようなものを作ってみます.
先程と同じディレクトリ構成で記述していきますと,
main.rb
require 'sinatra' get '/' do @title = "ハローワールド" erb :index end post '/sendtext' do @title = params[:message] erb :index end
index.erb
<h2>テスト</h2> <form method="post" action="/sendtext"> <input type="text" name="message"><input type="submit" value="Send"> </form>
layout.erb 先程と同様なので省略
main.rbではpost '/sendtext' doというgetからpostになっていることがわかると思います. これはpostで送られてきた処理に対して実行されるものです./sendtextとありますからlocalhost以下に/sendtextというurlが付加されてきた場合を指します.
つまりこの場合「sendtextというurlが末尾についてきてかつpostの場合はここを実行」ということになります.
中身はと言えば,@title = params[:message]
とありますね.index.erbで定義したinputのname名]で受けとれます.この場合は@title
に代入しているので
「erbで定義したinput textのname名の値をタイトルにする」ということになります.
erb :index
でこの処理が終ったら次に飛ばす場所を指定してます.今回は元の画面に飛んでもらえば良いのでこのような書き方になってます.
redirectでルートを指定できますが,そうすると変数が消えてしまうので,erb :index
という書き方にしました.
以上でしょうか.一先ず文字を出力したり入力したりといったことを簡単にまとめてみました. helperというのを使えばメソッドを定義して実行できるという便利なメソッドもありますが,疲れたのでここまでとします.