FlaskでWebアプリを作る。その3:見た目を作る
こんにちは、むさしです。 引き続きflaskでwebアプリを作っていきましょう。
前の記事: playwao.hatenablog.com
base.htmlを編集する
base.htmlを編集しましょう。 htmlについては僕はあまり詳しくないのでうまく説明できないので自分で調べて貰えばと思います。
また、デザインを楽にするために今回はBootStrapを使います。
こちらにアクセスしてbootstrapファイルをstaticフォルダに入れてもいいです。今回は面倒なのでcndを使います。 また、jQueryも使うので同様の方法で導入してください。
つまりファイル構造はこういう風になります。
flask_app |---static |---js | |---jquery-2.1.4.min.js | |---bootstrap.min.js |---css |---bootstrap.min.css |---app.css
app.css
はちょっと大きさを調整したい時などに使います。
準備ができたので早速base.html
を編集しましょう。
base.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>flask_app</title> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet"> <link href="http://localhost:8080/static/app.css" rel="stylesheet"> </head> <body> <header> <nav class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <a class="navbar-brand" href="#">読書履歴</a> </div> <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav"> <li><a href="books/index.html">書籍一覧</a></li> <li><a>記録一覧</a></li> </ul> </div> </div> </nav> </header> <div class="container-fluid"> <div class="row"> {% block body %}{% endblock %} </div> </div> <footer> <script src="http://code.jquery.com/jquery-latest.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.0/js/bootstrap.min.js"></script> </footer> </body> </html>
書籍一覧と記録一覧は後でリンクを入れます。とりあえず今はこのままにしておきます。
次に一覧を表示できるようにindex.htmlを作りましょう。 その際、templateファイルの階層は以下のようにします。
template |---base.html |---book |---index.html
早速index.htmlを作っていきます。
index.html
{% extends "base.html" %} {% block body %} <h1>書籍一覧</h1> <div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>ID</th> <th>タイトル</th> <th>著者・作者</th> <th>出版社</th> </tr> </thead> <tbody> {% for book in books %} <tr> <td>{{ book.id }}</td> <td>{{ book.title }}</td> <td>{{ book.auther }}</td> <td>{{ cbook.publisher }}</td> </tr> {% else %} <td>書籍が登録されていません</td> {% endfor %} </tbody> </table> </div> {% endblock %}
{% for book in books %}
はjnja2のfor文です。
このfor文は必ず最後に{% endfor %}
を指定する必要があります。
books
には書籍の情報が入っています。
この情報については後でpythonの方で指定します。
{% else %}
はもしbooks
に何も入っていない時に実行される関数です。
今回は「書籍が登録されていません」というメッセージが表示されます。
これでhtml部分は完成です。
ルーティングを行う
さて、HelloWorldを作った時しれっと@app.route("/")
をつかったと思うのですがこれがルーティーングです。
ルーティングで指定された場所に移動すると、その後ろにつく関数が実行されます。
これを使って見た目を作っていきます。
manage.pyにルーティングを追加します。
manage.py
@app.route("/book/index") def book_index(): """ 書籍一覧 """ books = Book.query.order_by(Book.id.asc()) return render_template("book/index.html", books=books)
これは書籍をデータベースから抜き取り一覧を表示させています。
まず、Book.query
で書籍のqueryを生成し、すべての書籍に関する情報を抜き出します。
その後のorder_by
で順番に並べ直します。
今回はBook.id.asc()
としておりBookのidを昇順に並べています。
最後にrender_template
を返しています。
このrender_template
の第一引数に表示させたいhtmlを入れます。
books=books
はhtmlを生成する際の引数を指定しています。
今回は左右同じ名前でわかりにくいですが、例えば
<tbody> {% for content in contents %} <tr> <td>{{ content.id }}</td> <td>{{ content.title }}</td> <td>{{ content.auther }}</td> <td>{{ content.publisher }}</td> </tr> {% else %} <td>書籍が登録されていません</td> {% endfor %} </tbody>
の時は
@app.route("/book/index") def book_index(): """ 書籍一覧 """ books = Book.query.order_by(Book.id.asc()) return render_template("book/index.html", contents=books)
とする必要があります。
サーバーを動かして確認する
早速できているか確認してみましょう。 サーバーを起動させて、http://localhost:8080/book/indexにアクセスします。
うまくできていると以下のように表示されます。
今回はまだ何も書籍に関するデータが入っていないので「書籍が登録されていません」と表示されています。 うまくいたようです。
次回は書籍データのCRUDとフォーム作成について話していきます。
FlaskでWebアプリを作る。その2:Flask-Migrationでデータベースを作る
こんにちは、むさしです。 引き続きflaskでwebアプリを作っていきましょう。
前の記事: playwao.hatenablog.com
データベースを利用したWebアプリを作る
これからデータベースを使ったアプリを作っていきましょう。 はじめに話したようにデータベースはSQLiteを使います。
今回は読書用の日記アプリを作ります。 作る必要があるであろうテーブルは以下の2つになります。
- 書籍
- 書籍名
- 作者・著者
- 出版社
- 読んだ記録
- 日付(自動)
- 読んだ本
- 感想
また、ファイル構造を以下のように構成します。
. ├── manage.py ├── models.py ├── form.py ├── view.py ├── static | └── app.css └── templates ├── index.html └── base.html
manage.py
は名前こそ変えましたがその1で作ったやつのapp.py
と同じです。
model.py
はfalskでデータベースのデータを扱い際の???いい言葉が思いつかない???になります。
view.py
はブラウザなどとやりとりするのに相手のレスポンスに対して特定の関数を呼び出し、適切なレスポンスを返す機能を実装します。
form.py
は書籍や記録などを入力する際のフォーム用のクラスを設定します。
また、header.html
はbase.html
と名前を変えます。変えなくても問題はありません。
staticフォルダにはapp.css
というファイルを入れておきます。
データベース関係の準備
SQLAchemyのインストール
データベースはSQLiteを使います。macにはデフォルトで入いるのでそのままで大丈夫です。 mySQLなど好みのデータベースを使ってください。
ORマッパとしてSQL-Alchemyを使います。ORマッパとはwikipediaによると、
オブジェクト関係マッピング(英: Object-relational mapping、O/RM、ORM)とは、データベースとオブジェクト指向プログラミング言語の間の非互換なデータを変換するプログラミング技法である。オブジェクト関連マッピングとも呼ぶ。実際には、オブジェクト指向言語から使える「仮想」オブジェクトデータベースを構築する手法である。
とのことです。 僕の中ではpythonでデータベースにアクセスするのにSQLで記述しなくて良くなる便利なもの程度の認識です^^;
とにかくインストールしましょう。またこの時一緒にFlask-SQLAchemy
もインストールしましょう。SQLAchemyのセッションなど管理できるので自前で作る必要がなく楽できます。
$ pip install sqlalchemy $ pip install flask-sqlalchemy
Flask-Migrateのインストール
データベースを使うのに直接SQLiteを操作してテーブル作るのもいのですが、使うテーブルを変えたい時、新しいテーブルを追加したい時などにサーバーとデータベースをどっちも修正するのは面倒です。
また、ローカル環境で開発して本番のサーバーにあげる時初めからデータベースにテーブルを打ち込まなくていいというメリットもあります。
そこでFlask-Migrate
を使います。これを使えばテーブルの修正や追加などをサポートしてくれます。
まずはpipでインストールします。
$ pip insatll flask-migrate
するとダウンロード完了です。
pip freeze
で確認してみてください。
$ pip freeze alembic==0.7.6 Flask==0.10.1 Flask-Migrate==1.4.0 Flask-Script==2.0.5 Flask-SQLAlchemy==2.0 itsdangerous==0.24 Jinja2==2.7.3 Mako==1.0.1 MarkupSafe==0.23 SQLAlchemy==1.0.6 Werkzeug==0.10.4 wheel==0.24.0
色々増えていますが、Flask-ScriptやMakoなどはFlask-Migrateをインストールした際に一緒にダウンロードされただけです。
ちなみにこのflask-migrateはflaskのオイラリー著者が作ったそうです。
モデルとマイグレーションの設定
モデルを作る準備ができたので早速作っていきましょう。
まずはモデルを作っていきます。
models.py
を以下のよう編集します。
models.py
#!/usr/bin/env python # coding: utf-8 from flask.ext.sqlalchemy import SQLAlchemy from sqlalchemy import Column, Integer, Unicode, UnicodeText, ForeignKey from sqlalchemy.orm import relationship, backref from datetime import datetime db = SQLAlchemy() class Book(db.Model): """ 書籍モデル """ __tablename__ = "books" id = Column(Integer, primary_key=True) title = Column(Unicode(255)) auther = Column(Unicode(255)) publisher = Column(Unicode(255)) #初期化 def __init__(title, auther, publisher): self.title = title.title() self.auther = auther.title() self.publisher = publisher.title() class Diary(db.Model): """ 感想モデル """ __tablename__ = "diaries" id = Column(Integer, primary_key=True) date = Column(Unicode(255)) book_title = Column(Unicode(255), ForeignKey('books.title')) impression = Column(UnicodeText) #書籍とのリレーションを作成 book = relationship("Book", backref=backref('diaries', order_by=id)) #初期化 def __init__(book_title, impression): self.date = datetime.utcnow().strftime( '%Y-%m-%d %H:%M:%S' ) self.book_title = book_title.title() self.impression = impression.title()
まずは
from flask.ext.sqlalchemy import SQLAlchemy db = SQLAlchemy()
として、SQLAchemyを使う準備をします。
次に書籍のモデルを作っていきます。
from sqlalchemy import Column, Integer, Unicode class Book(db.Model): """ 書籍モデル """ __tablename__ = "books" id = Column(Integer, primary_key=True) title = Column(Unicode(255)) auther = Column(Unicode(255)) publisher = Column(Unicode(255)) #生成された時、呼び出される def __init__(title, auther, publisher): self.title = title.title() self.auther = auther.title() self.publisher = publisher.title()
__tablename__
にはデータベースのテーブル名を入れてください。
これでどのテーブルを使うか指定します。
また、今回はmigrateもするのでその際のテーブル名になります。
その下はテーブルのカラムとその定義を書いていきます。
例えばid = Column(Integer, primary_key=True)
ならid
がカラム名に、Integer, primary_key=True
がカラムの定義にあたります。
Integer
はこのカラムに整数が入ることを表しています。primary_key=True
は入る値がカラムに唯一しか存在しないことを表しています。明記していませんがこの値は自動でナンバリングされます。なので、例えば新たにデータを入力すればidが自動で番号付けされます。
title = Column(Unicode(255))
などにあるUnicode(255)
は値にunicodeを取れることを表しています。文字数は255文字までとれます。Unicode
の代わりにString
を使うと日本語などを入力した時エラーがでるので注意が必要です。僕はめんどくさいので全部Unicode
にしています^^;
__init__
とはBookクラスが生成された時呼び出される関数です。今回は引数にtitle, auther, publisher
を取っています。この関数は生成時に呼び出されるものです。
self.title = title.title()
のtitle()
はクラスが生成された時に取った引数を代入します。この例はtitle、titleでこれはわかりにくいですが^^;
つまりnewBook = Book("坊ちゃん", "夏目漱石", "青空文庫")
ならば、
title = "坊ちゃん" auther = "夏目漱石" publisher = "青空文庫"
と各変数に代入されます。
同様に感想についてもモデルを作っていきます。
from sqlalchemy import Column, Integer, Unicode, UnicodeText, ForeignKey from sqlalchemy.orm import relationship, backref from datetime import datetime class Diary(db.Model): """ 感想モデル """ __tablename__ = "diaries" id = Column(Integer, primary_key=True) date = Column(Unicode(255)) book_title = Column(Unicode(255), ForeignKey('books.title')) impression = Column(UnicodeText) #書籍とのリレーションを作成 book = relationship("Book", backref=backref('diaries', order_by=id)) #生成された時、呼び出される def __init__(book_title, impression): self.date = datetime.utcnow().strftime( '%Y-%m-%d %H:%M:%S' ) self.book_title = book_title.title() self.impression = impression.title()
先ほどと同じようなところは説明を省かせていただきます。
book_title = Column(Unicode(255), ForeignKey('books.title'))
とbook = relationship("Book", backref=backref('diaries', order_by=id))
はリレーションを定義しています。今回作成しているWebアプリは書籍一つに関していくつも感想や記録をつけることができます。このように書籍の下にいくつもの感想がくる構造を取ることを一対多の関係にあるといいます。これを関連付けているのがリレーションです。
book_title = Column(Unicode(255), ForeignKey('books.title'))
のForeignKey
は外部キーといいます。このForeignKey
の引数としてリレーション先のテーブル名とそのカラム名を(テーブル名).(カラム名)
を指定します。
book = relationship("Book", backref=backref('diaries', order_by=id))
はBook
とDiary
という2つのクラスのリレーションをrelation()
関数を使って別個に定義します。この時relation()
関数をBookの方に
class Book(db.Model): ... diary = relation("Diary", order_by=Diary.id, backref="books")
と定義も出来ます。どちらで書くこともできますが僕は基本的に子要素のところで定義します。
また、__init__
でdate
についてself.date = datetime.utcnow().strftime( '%Y-%m-%d %H:%M:%S' )
としていますがこれはクラスを生成した時に時間を自動で入力してくれるようにしています。
これ以外にも例えばユーザー登録時にランダムidを発行したくなった時はuuid
を使って
class User(db.Model): id = Column(Integer, primary_key=True) uuid = Column(Unicode(255)) name = Column(Unicode(255)) def __init__(name) self.uuid = uuid.uuid4() self.name = name.title()
とできます。
今度はmange.py
を編集していきます。
manage.py
#!/usr/bin/env python # coding: utf-8 import sys reload(sys) sys.setdefaultencoding('utf-8') from flask import Flask, render_template from model import db from flask.ext.script import Manager, Server from flask.ext.migrate import Migrate, MigrateCommand app = Flask(__name__) #デバッグ app.config['DEBUG'] = True #秘密キー app.secret_key = 'development key' #データベースを指定 app.config['SQLALCHEMY_DATABASE_URI'] = 'SQLite:///diary.db' app.config['SQLALCHEMY_NATIVE_UNICODE'] = 'utf-8' db.init_app(app) db.app = app @app.route("/") def hello(): return render_template("index.html") migrate = Migrate(app, db) manager = Manager(app) manager.add_command('db', MigrateCommand) manager.add_command('runserver', Server(host='localhost', port='8080')) if __name__ == "__main__": manager.run()
まず、はマイグレーションに必要なものをインポートします。
from flask.ext.script import Manager, Server from flask.ext.migrate import Migrate, MigrateCommand
そのあとデータベースを指定します。
app.config['SQLALCHEMY_DATABASE_URI'] = 'SQLite:///diary.sqlite3' app.config['SQLALCHEMY_NATIVE_UNICODE'] = 'utf-8' db.init_app(app) db.app = app
その後、MigrateオブジェクトとManagerオブジェクトを生成します。
add_command
でコマンドラインからマイグレーションの処理を行います。
このときmanager.add_command('runserver', Server(host='localhost', port='8080'))
の部分はこのflask-migrateを適応するとなぜかホストやポート番号を設定できないのでこれで無理やり設定しています^^;
もっといい方法があればいいのですが…
ちなみにapp.config['SERVER_NAME'] = 'localhost'
ではうまくいきませんでした。
マイグレーションを行う
マイグレーションする準備できたので早速マイグレーションを行なっていきましょう。
まず、ちゃんと正しく動いているか確認するためにpython mange.py
を実行させてみましょう。すると以下のように説明が出てきます。これが出てきたら正しくでいている証拠です。
$ python manage.py usage: manage.py [-?] {shell,db,runserver} ... positional arguments: {shell,db,runserver} shell Runs a Python shell inside Flask application context. db Perform database migrations runserver Runs the Flask development server i.e. app.run() optional arguments: -?, --help show this help message and exit
また、python manage.py db --help
を実させましょう。
$ python manage.py db --help usage: Perform database migrations Perform database migrations positional arguments: {upgrade,heads,show,migrate,stamp,current,merge,init,downgrade,branches,history,revision} upgrade Upgrade to a later version heads Show current available heads in the script directory show Show the revision denoted by the given symbol. migrate Alias for 'revision --autogenerate' stamp 'stamp' the revision table with the given revision; don't run any migrations current Display the current revision for each database. merge Merge two revisions together. Creates a new migration file init Generates a new migration downgrade Revert to a previous version branches Show current branch points history List changeset scripts in chronological order. revision Create a new revision file. optional arguments: -?, --help show this help message and exit
この中のmigrate
とupgrade
を主に使います。
まず、データベースを初期化します。
$ python manage.py db init Creating directory ~/flask_app/migrations ... done Creating directory ~/flask_app/migrations/versions ... done Generating ~/flask_app/migrations/alembic.ini ... done Generating ~/flask_app/migrations/env.py ... done Generating ~/flask_app/migrations/env.pyc ... done Generating ~/flask_app/migrations/README ... done Generating ~/flask_app/migrations/script.py.mako ... done Please edit configuration/connection/logging settings in '~/flask_app/migrations/alembic.ini' before proceeding.
こうするとmanage.py
と同じディレクトリにmaigration
というフォルダができています。
ここでマイグレーションを管理、実行します。
マイグレーションファイルを製作しましょう。
$ python manage.py db migrate INFO [alembic.migration] Context impl SQLiteImpl. INFO [alembic.migration] Will assume non-transactional DDL. INFO [alembic.autogenerate.compare] Detected added table 'books' INFO [alembic.autogenerate.compare] Detected added table 'diaries' Generating ~/flask_app/migrations/versions/299d7e501a6e_.py ... done
するとmigration/versions
になんらかのpythonファイルが生成されています。
最後にこれをupgradeしてsqliteに適応させましょう。
$ python manage.py db upgrade INFO [alembic.migration] Context impl SQLiteImpl. INFO [alembic.migration] Will assume non-transactional DDL. INFO [alembic.migration] Running upgrade -> 299d7e501a6e, empty message
これで完了です。daiary.db
というデータベースが生成されていると思うので中身を確認してみましょう。
$ sqlite3 diary.db SQLite version 3.8.5 2014-08-15 22:37:57 Enter ".help" for usage hints. sqlite> .table alembic_version books diaries sqlite> .schema CREATE TABLE alembic_version ( version_num VARCHAR(32) NOT NULL ); CREATE TABLE books ( id INTEGER NOT NULL, title VARCHAR(255), auther VARCHAR(255), publisher VARCHAR(255), PRIMARY KEY (id) ); CREATE TABLE diaries ( id INTEGER NOT NULL, date VARCHAR(255), book_title VARCHAR(255), impression TEXT, PRIMARY KEY (id), FOREIGN KEY(book_title) REFERENCES books (title) );
無事出来ているようです。
次回はテンプレートファイルとCRUDを実装していきたいと思います。
次の記事: playwao.hatenablog.com
FlaskでWebアプリを作る。その1:HelloWorldを作る
こんにちは、playwaoのむさしです。 最近、仕事でFlaskを触る事になったそれらについてのまとめを書こうかと思います。
FlaskでWebアプリを作る
まず、何よりも日本語の資料が中々存在しません。日本だとPHPやRubyの方がメジャーのようでそちらの資料は豊富に存在します。また、僕はWebアプリケーション作りが今回が初めてという事もあり、かなり苦労しました。データベースなにそれ?状態から始めたので大変でした^^;
そんなこんなで2週間ずーっとFlask勉強していました…僕みたいな迷子を生まないために、今回記事を書いていきたいと思います。
内容としては、はじめの一歩からJSONでやりとりできるようになるところまでを想定しています。そこそこ内容があり長くなるので複数記事に渡り書かせていただきます。 また、内容も1ヶ月前の僕のように初めてPythonでWebアプリ作るという人向けに書いていく予定なのである程度経験ある人は適当に読み飛ばしながらご覧ください。 またまた、当方pythonだけでなくプログラミングもかなり初心者なのでカスみたいなコード書いているのですが、その辺も多めに見ていただけると幸いです。こっちの方がいいコードだお、ここのコード間違っているお、みたいなのがあれば教えていただけると僕の勉強にもなるのでありがたいです。
webアプリやサーバーは個人ゲーム開発者には関係ないのか?
webアプリはゲームと関係ない?ように思います。特に個人開発の人はそんなにコストを掛けれないはずです。サーバーだってタダでないし維持するのも大変です。ただ、僕は最近関係ないとは言えないのではないかと感じています。
例えばほんの2、3年前の頃は3DCGで個人開発なんて想像もしませんでしたが、今は個人開発でもヒットを出してるものに3DCGを使っているものが増えてきています。 個人だから無理という時代は徐々に終わってきてるのです。
そりゃガチガチなソシャゲは無理でも、例えばradisと組みあわせてリアルタイムランキングを作ったり、「片道勇者」というフリーゲームでは同じ世界で遊んでいる人が敵に倒されたら画面左上にちょこっと通知がきます。先ほど例にあげた3DCGも特定のもののみ3DCGで作っていたり、視点が1視点からしかないとか、それぞれのもつフルの性能でなく、実現できそうなちょっとしたところに使ってみるというやり方ができるようになってきていると思います。
これをきっかけにwebアプリというものに挑戦してみてはいかがでしょうか?
Flaskとは?
FlaskとはPythonのWebアプリ用の マイクロフレームワーク です。 公式によるとmicroとは…
『micro』とは何でしょうか。私にとっては、microframework における「micro」は、 フレームワークの簡易性やフレームワークの小さいものとうだけでなく、フレームワークで 書かれた、複雑さとアプリケーションのサイズに制限をかけたものです。 ひとつのPythonファイルにアプリケーション全体を持つことも可能です。 この制限によりMicroframeworkが親しみ易く、簡単なものと感じることでしょう
つまり、ひとつのファイルにアプリケーション全体を入れることができる小さいアプリケーションを作れる!ということみたいです。 ただ、Flakでは大きいアプリケーションが作れないわけではありません。後々説明していきます。
環境構築
Pythonのインストール
私の開発環境はMacOS10.10.3です。
まず、pythonとpipがインストールされている前提で話を進めます。
使うのはpython2.7です。特に理由はないのですが会社で触った時は2.7だったので同じ状態で作っていきたいからです。
FlaskはPython3にも対応済みだのでそちらを利用しても大丈夫です。ただ、使用するモジュールが対応していない可能性があるので注意してください。
バージョンの確認はpython --version
で確認できます。
$ python --version Python 2.7.9
また、仮想環境構築のためvirtualenvをpipで入れてください。 詳しい使い方はこちらを参考にしてください。
Flaskのインストール
早速、Flaskをインストールしましょう。
$ pip install Flask
ちゃんとインストールされているかpip freeze
で確認します。
$ pip freeze Flask==0.10.1 itsdangerous==0.24 Jinja2==2.7.3 MarkupSafe==0.23 Werkzeug==0.10.4 wheel==0.24.0
Jinja2とかWerkzeugなどはFlaskをインストールされる際に一緒にインストールされます。
これで準備万端です。早速作っていきましょう。
FlaskでHelloWorld
アプリを作るディレクトリを作成し、そこに移動します。
$ mkdir flask_app $ cd flask_app
app.py
を作ります。これは公式のhellworldをちょっといじったものです。
app.py
#!/usr/bin/env python # coding: utf-8 import sys reload(sys) sys.setdefaultencoding('utf-8') from flask import Flask app = Flask(__name__) @app.route("/") def hello(): return "Hello World!" if __name__ == "__main__": app.run()
この中で公式のものに付け足した部分は
#!/usr/bin/env python # coding: utf-8 import sys reload(sys) sys.setdefaultencoding('utf-8')
です。これを記述したのはpythonでよく起こるunicode errorを回避するためです。今後日本語でコメントとか書いたり日本語のデータを保存する際によく起こります。僕もまだあまりよくわかっていないのでもう少しちゃんと分かりたいです。
@app.route("/")
は関数の前に書くことができる修飾詞です。この場合だと http://(ホスト名)/
にアクセスするとhello()
が実行されます。
…まあ、この程度の認識で多分問題ないはずです^^;僕もこれ以上は理解していないです。
さて、これでもうアプリは起動できるので、python app.py
を実行しましょう。多分127.0.0.1:5000でウェブサーバーが起動します。
$ python app.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
ブラウザでhttp://127.0.0.1:5000/
を開けてみましょう。
無事"Hello World!"が出ることを確認できました。 サーバーはCTRL+Cで終了します。
テンプレートの導入
このままではwebサイトぽくないのでテンプレートを導入します。 僕は以下のようにディレクトリを構成します。
. ├── app.py ├── static └── templates ├── index.html └── header.html
templatesにindex.htmlとheader.htmlを作ります。 テンプレートエンジンとしてflaskをインストールした時に一緒についてきた Jinja2 を使います。htmlファイルにある{{}}がそれです。
header.html
<!DOCTYPE html> <html> <head> <title>flask_app</title> </head> <body> {% block body %}{% endblock %} </body> </html>
index.html
{% extends "header.html" %} {% block body %} <h1>Hello World!</h1> {% endblock %}
また、app.pyもこれに合わせて編集します。
flaskからrender_templateをインポートしてhello関数がrender_template("index.html")
を返すようにしてください。
こうすることでhttp://(ホスト名)/
を実行するとindex.htmlとheader.htmlを繋ぎ合わせたものが返ってきます。
app.config['DEBUG'] = True
とすることでファイル更新した時に自動で再起動したり、エラーが発生した場合デバッグ内容が表示されたりして非常に便利です。
app.py
#!/usr/bin/env python # coding: utf-8 import sys reload(sys) sys.setdefaultencoding('utf-8') from flask import Flask, render_template app = Flask(__name__) app.config['DEBUG'] = True @app.route("/") def hello(): return render_template("index.html") if __name__ == "__main__": app.run()
app.pyを実行して、http://127.0.0.1:5000/
をブラウザで開いてください。
無事実行できました。 ページのソースを見てもらうと
<!DOCTYPE html> <html> <head> <title>flask_app</title> </head> <body> <h1>Hello World!</h1> </body> </html>
となっており先ほど述べた通り2つのhtmlファイルが結合しているのがわかります。
とりあえず今回はこれで終了です。 次回からはよりアプリケーションらしく仕上げていきます。
次の記事: playwao.hatenablog.com
p.s.
はてなのマークダウンだとコードの左上にどのファイルを使ったか書けないのでソースコードの上にapp.py
みたいに書いてあるのですがもっといい方法はないのでしょうか?
BeautiflSoup + SQLAlchemyを使ってEvernoteの全ノートのテキストを抽出してSQLiteに保存する
こんにちは。むさしです。 しばらく更新してませんでしたが再開していきます^^;
最近はゲーム開発は止まっていて、仕事の関係でpythonを触っています。
evernoteの全文を抽出する必要がありその時に使った方法を公開します。 一番初めに思いつくことはEvernoteAPIを使う方法ですが色々面倒ですよね。 そこで、全ノートをhtml形式で出力してBeautifulSoupでスクレイピングする方法を紹介していきます。
Evernoteの全ノートをhtml形式で出力する
まず、Command + A
で全ノートを選択します。そこからノートをエクスポートします。
出力する形式はhtmlを選択してください。
今回はこれをmynoteとしてデスクトップに保存します。
mynoteのindex.htmlには出力したファイル全てのノートの目次になっていて、各htmlファイルへのリンクがあるのでそれを利用します。
手順としては
- index.htmlから出力するノートのurlを抽出する。
- url先からテキストを抽出する。
- それをSQLiteに保存する。
というものです。
BeautifulSoupを使ってスクレイピングする
そもそもスクレイピングとはwebサイトから特定の情報を抽出したりする行為です。スクレイピングできるpythonのモジュールはいくつかありますが今回はBeautifulSoupというものを使っていきます。
まず、BeautifulSoupでインストールしましょう。
$ pip install beautifulsoup4
次にBeatifulSoupオブジェクトを作成しましょう。
import urllib2 from bs4 import BeautifulSoup html = urllib2.urlopen("http://~ ~ ~") soup = BeautifulSoup(html)
これでスクレイプする準備が出来ました。
あとは詳しくは公式ドキュメントをご覧ください。
日本語で訳されているのでとっつきやすいと思います。
今回使うのはsoup.get_text()
とsoup.find_all("a")
とnote_url.get("href")
のみです。
SQLAlchemyを使ってデータベースに保存する
SQLAlchemyとはORマッパーと言って、SQLで記述しなくてもデータベースとやりとりができる便利なやつです。 インストールはpipで行いましょう。
$ pip install sqlalchemy
全て説明すると長くなるのでこちらを確認ください。
また、こちらの日本語訳もありますがかなり古いのです。 ただ、英語よか読みやすいと思うのでこちらも参考にしてください。
http://omake.accense.com/static/doc-ja/sqlalchemy/ormtutorial.html
今回はメインはBeautiflSoupに関してなので後日分けて説明します。
Evernoteの文章を抽出する
これで準備は出来たのでスクレイピングしていきましょう。
まずはノートから文章のみを抽出して返す関数をつくります。
def scrape_evernote(url): tmp_url = "file:///(ノートのディレクトリ)" + url.encode('utf-8') html = urllib2.urlopen(tmp_url) soup = BeautifulSoup(html) all_items = soup.get_text() note = "" for word in all_items: note = note + word return note
最初の3行はBeautifulSoupオブジェクトを生成します。
all_items = soup.get_text()
でurl先の全文を取得します。
そのあとの
note = "" for word in all_items: note = note + word
という部分は、get_text()
で取得できる文字は一文字づつ配列に入っているので配列全てを結合させる必要があります。
抽出したテキストをSQLiteに保存する
次に抽出したテキストをSQLiteに保存する関数を作ります。
Base = sqlalchemy.ext.declarative.declarative_base() db_url = "sqlite+pysqlite:///evernote.sqlite3" engine = sqlalchemy.create_engine(db_url, echo=True) Base.metadata.create_all(engine) # セッションを作成 Session = sqlalchemy.orm.sessionmaker(bind=engine) session = Session() #evernoteのモデルクラス class Evernote(Base): __tablename__ = 'mynote' id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) title = sqlalchemy.Column(sqlalchemy.String) note = sqlalchemy.Column(sqlalchemy.String) #...(略)... def scrape(): #indexから全ノートのurlを取得する index_url = "file:///(ノートのディレクトリ)/index.html" index_html = urllib2.urlopen(index_url) index_soup = BeautifulSoup(index_html) all_url = index_soup.find_all("a") for note_url in all_url: title = note_url.get_text() note = scrape_evernote(note_url.get("href")) evernote = Evernote(title=title, note=note) session.add(evernote) session.commit()
初めにBase
を作ります。(あんまりよくわかっていません^^;)
そのあと、ノートのモデルを作成します。
class Evernote(Base): __tablename__ = 'mynote' id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) title = sqlalchemy.Column(sqlalchemy.String) note = sqlalchemy.Column(sqlalchemy.String)
今回はシンプルにノートのタイトルと内容だけ保存します。
db_url = "sqlite+pysqlite:///evernote.sqlite3" engine = sqlalchemy.create_engine(db_url, echo=True) Base.metadata.create_all(engine) # セッションを作成 Session = sqlalchemy.orm.sessionmaker(bind=engine) session = Session()
あとはSQLiteの保存場所をdb_url = "sqlite+pysqlite:///evernote.sqlite3"
で指定してセッションを作成します。
Base.metadata.create_all(engine)
はevernote.sqlite3
が存在しなかったら自動でevernote.sqlite3
を作ってくれるます。
また、モデルクラスがあれば自動でテーブルの作ってくれて大変便利です。
最後にはindex.html
から各ノートのタイトルとurlを取得します。
index.htmlの各ノートへのリンクは
<a href="ノートのurl">ノートのタイトル</a>
という構成になっているのでindex_soup.find_all("a")
で全部のaタグを取得します。
各タグはそれぞれ配列で格納しているので、取り出してaタグからリンク先のurlとタイトルを取得します。
そのurlから先ほど作ったscrape_evernote()
を使ってテキストを抽出します。
最後にコミットしてSQLiteに保存します。
これで抽出は完了です。
SQLiteで出力ではなくて例えばtxtデータに出力したい場合は
def scrape_txt(): file = open('evernote_text.txt', 'w') #indexから全ノートのurlを取得する index_url = "file:///(ノートのディレクトリ)/index.html" index_html = urllib2.urlopen(index_url) index_soup = BeautifulSoup(index_html) all_url = index_soup.find_all("a") for note_url in all_url: title = note_url.get_text() file.write(title) note = scrape_evernote(note_url.get("href")) file.write(note) file.close()
とすれば可能です。 もちろんcsv形式の出力も出来ます。
まとめ
初めの方にも書きましたが大まかの手順は
となっています。 今回はテキストのみ抽出しましたが、ノート内の画像がノートのタイトルと同じ名前のフォルダに保存されています。 これをうまく使えばevernote内の画像を全て抽出することも可能だと思います。
完成データをgitにあげています。 https://github.com/musashi13z/evernote-scrape
Unityでとりあえずポーズ画面を作りたい
こんにちはむさしです。 僕の担当は一応イラスト関連なんてすが、Unityも少しづつ勉強しています。 勉強していてこんな情報欲しいなとか思ったことなどを色々書いていきたいと思います。
1. ものすごい簡単なポーズ画面の作り方
UnityのHowTo本とか読んでも中々ポーズ画面の作り方が載っていません。少なくとも僕は見たことがありません!
このポーズ画面は色々と使えて、格ゲーなどのカットインエフェクトやRPGのお店の画面などの処理にも使える重要な機能の一つです。 ポーズ画面等の処理は本質的に言うと今あるシーンから一時的に(擬似的な)別のシーンに移動して再び元の状態に戻る処理の事です。だから、ただのシーンの切り替えと同じ処理をしては元の状態には戻れません。なぜなら、切り替える際にキャラクターの状態パラメータなどが保管している必要があるからです。 今回は自分が色々調べたり人から聞いたりしたポーズ画面についてのまとめを書いていきます。
一番多いやり方はTime.timescale = 0にすることです。これをすることでアニメーションやリジッドボディなどほとんどのものを止めてしまいます。及ぼす効果の範囲が大きく当然問題も発生します。後半にそれについて話します。 デフォルトではTime.timescaleの値は1でその値を小さくすることでスローモーションに、逆に値を大きくすることで早送り状態になります。 以下のように実装するのが一般的な方法です。
まず、上下に運動するハッピーフェイスを作ります。 ハッピーフェイスはこれです。ニコちゃんマークです。
僕はアメリカかぶれなんでハッピーフェイスと呼んでいます(笑)
まずスクリーンとアセットはこんな感じになります。
なんとなくわかるように同じ名前なやつを同じ名前のスクリプトにコンポーネントしています。 例えばhappyface1にhappyface1.csをコンポーネントしています。 happyface1とhappyface2、pause1とpause2がそれぞれ2つあるのはTime.timescale=0にすることによって発生する現象を説明するためです。
まずはhappyface2.csを以下のような感じにします。
public class happyface2 : MonoBehaviour { float dy = 1f; void Start () { dy = Time.deltaTime * 1f; } void FixedUpdate () { Vector2 v = transform.position; v.y += dy; if (v.y > 3f) { v.y = 3f; dy *= -1; } if (v.y < -3f) { v.y = -3f; dy *= -1; } transform.position = v; } }
Time.deltaTimeについては後々説明します。とりあえずそんなもんなんだなていどの認識でいいです。 FixedUpdateを使ってhappyfaceが上下に移動するようにしています。
次にpause2.csを作ります。 僕は面倒なんでスプライトをそのままボタンにします。 NGUIとか使うとかっこいいと思います。 スクリプトは以下のようになります。
public class Pause2 : MonoBehaviour { void Start () { } void Update () { if (Input.GetMouseButtonDown(0)) { Vector2 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition); Collider2D c = Physics2D.OverlapPoint(pos); if (c == this.collider2D) { if (Time.timeScale == 0) { Time.timeScale = 1; } else { Time.timeScale = 0; } } } } }
マウスでクリックしてそれがオブジェクト(今回はボタンのスプライト)のcolliderに当たったらTime.timeScaleが1になら0に、0なら1になるようにしています。 つまり動いている時に押すと止まり、止まっている時に押すと動き出します。
…が、これではこのままではうまくいかない場合もあります。 たとえばhappyface1.csをこんな感じにすると…
public class happyface1 : MonoBehaviour { float dy = 1f; void Start () { dy = Time.deltaTime * 1f; } void Update () { Vector2 v = transform.position; v.y += dy; if (v.y > 3f) { v.y = 3f; dy *= -1; } if (v.y < -3f) { v.y = -3f; dy *= -1; } transform.position = v; } }
先ほどのポーズでは止まりません。 初めの方にTime.timescale = 0にすることでほとんどのものの動きを止めてしまうと説明しましたが、実はUpdateは実行できます。 これも止めたい場合は止めたい対象にポーズフラグを入れればいいです。 今回の場合だとハッピーフェイスのUpdateにポーズフラグを入れる必要があります。 では、Updateも止めることができるボタンを実装しましょう。 pause1.csのスクリプトを以下のものにします。
public class Pause1 : MonoBehaviour { public bool is_pause = false; void Start () { } void Update () { if (Input.GetMouseButtonDown(0)) { Vector2 pos = Camera.main.ScreenToWorldPoint(Input.mousePosition); Collider2D c = Physics2D.OverlapPoint(pos); if (c == this.collider2D) { if (is_pause) { Time.timeScale = 1; is_pause = false; } else { Time.timeScale = 0; is_pause = true; } } } } }
今現在ポーズかどうかをis_pauseで示しています。 それ以外にもう一度ボタンを押したらポーズが解除されるようにしました。 一方、ハッピーフェイスはポーズフラグを利用してreturnを返すようにします。 次にhappyface1.csを
public class happyface1 : MonoBehaviour { public Pause1 pause; float dy = 1f; void Start () { dy = Time.deltaTime * 1f; this.pause = GameObject.FindGameObjectWithTag("Pause1").GetComponent<Pause1>(); } void Update () { if (this.pause.is_pause) { return; } else { Vector2 v = transform.position; v.y += dy; if (v.y > 3f) { v.y = 3f; dy *= -1; } if (v.y < -3f) { v.y = -3f; dy *= -1; } transform.position = v; } } }
この時、ポーズボタンのタグとスクリプトの名前を「Pause1」にしておいてください。 また、ポーズボタンをプレハブにしておいてください。 毎回ゲームオブジェクトを探すのは負担がかかるのでStartであらかじめゲームオブジェクトを探しておいてGetComponentしておくことで負担を減らしています。っていうか、Start以外に書くと警告がでます(ノ゚ο゚)ノ this.pause.is_pauseがtrueなら止まり、falseなら実行されます。 これでUpdateの動きを止めることができます。
逆にこのUpdateのみ動くのを利用すればポーズ画面のボタンなどを実装できます。 あらかじめポーズ画面のプレハブを作っておき、ポーズボタンが押された時生成するようにすればいいと思います。 uGUIやNGUIなどを使うのもいいですが僕は使い方わからないのでこの処理で乗り切っています(笑)
完成したらこんな風になります。たぶん。
上のPauseがis_pauseフラグありで、下はTime.timescaleのみいじっています。 左のhappy faceがUpdateで左のhappy faceはFixedupdateで移動しています。
2.Time.deltaTimeについてと問題の回避方法
Time.timescale = 0にするのは影響が大きく様々なものに影響を及ぼします。特に影響が大きいのはTime.deltaTimeが0になってしまうことです。まず、Time.deltatimeについて詳しく話していきます。
このTime.deltaTimeとはフレームの切替速度です。Unityではフレームを切り替えて表示して物を動かしています。このフレームの切替速度はゲームが動いているハードをに依存しています。つまり、切替速度が速いマシンでは一秒で切り替えられるフレームが多くなり早くなってしまいます。例えばある物体が毎フレームごとに移動する時、切替速度が速いと早く移動して、切替速度が遅いと遅く移動することになります。これでは問題が出てしまうのでそれを調整するために物体の毎フレームごとに移動する大きさにかけて調整しいてやります。
void Start(){ dx = Time.deltaTime * 1f; }
dxの大きさは適当に調整してください。今回は面倒なので1fにしています。これでマシンの切替速度に関係なく一定の速さで物体が移動させることができます。 これは、歩く人に例えるとわかりやすいです。一歩を踏み出すのが速い場合は歩幅を小さくして歩き、一歩を踏み出すのが遅い場合は歩幅を大きくして歩きます。ちょうど下の図のようなイメージです。
先ほどTime.timescale = 0にするとTime.deltaTimeも0になってしまいます。これでは先ほど例にあげた場合だと動きますが、例えばポーズ画面でUIを動かそうとする場合にTime.deltaTimeが0だと動きません。 これを解消するにはポーズに入る前にTime.deltaTimeの値をあらかじめ保管しておきそれを代入する必要があります。
3.その他に起こり得る問題など
*NGUIはTime.timescaleの大きさに関わらず動く
UIでよく使われるプラグインとしてNGUIというものがあります。これはNGUIが独自にTime.deltatimeの値を保管して持っているので、Time.timescaleの値に関係なく動きます。
*iTweenは動かない
アニメーションで動かす時によく使いプラグインですがこれも動きません。 どうやったら動くのか…もう少し調べて記事にしてみます。 まあ、どこかにTime.deltatimeの値を保管しとけはいいんですけどね。
*アニメーションは動かない
アニメーションも動かないので当然アニメーションイベントも動きません。あまり使う機会はないのではと思いますが問題がある場合はググってみてください。もう私の限界を超えています(^o^)/
4.おわりに
いかがでしたでしょうか?
正直説明できるほどUnityに関する知識はないですが必死になって書いたんで参考にしていただければと思います。
ワンダーブロックはゆるい雰囲気と丁寧な作り込みが組み合わさったゲームだ!
こんにちは!むさしです。
今日はゲームの紹介です。 これはすごい!と思ったのでこれを誰かに伝えたくて! 昨日からサービスが開始したワンダーブロックを少し遊んでみた感想です。
結論から言うと非常に完成度が高いということです! ここまで丁寧に作り込んでいるソーシャルゲームを自分は初めてみました。
ゲームの簡単な説明は他の記事の方が充実していると思うのでそちらを見てみてください。 一応簡単に説明しておきます。 ルールは簡単でブロックを配置してパネルを壊して敵を攻撃して敵を倒すというものです。
ちなみに敵はこいつです。
動画で見て欲しいんですがこのゲームをプレイしてる段階で丁寧さを感じます。 効果音やエフェクト、画面遷移まで細かくこだわって作っています。 しかもステージによってUIも変わっているところもあります。
作り込みだけではありません、遊び心も満載です。 たとえばホーム画面にいるベス(ピンク髪の女の子)をタップすると裏にメッセージが書いてあります。 何回かやっているとメッセージが変わりビビ(紫髪の女の子)が現れます。
他にもアイテムを購入できるショップの画面では性格が随分変わったビビさん(?)がいます。 その彼女の足の先を見てみてください。 ショップの文字が彼女の足を避けています。 しかも、アイテム欄を下まで下げるとビビさんのパンツが見れます(ゲス顔)
これだけじゃありません。本当にどの画面を見ても何かしらが動いていて1時間経ってもまだまだ発見があります。 これスマホのスペックそこそこないとホーム画面で落ちるじゃないでしょうか(笑)
敵はハイクオリティーな丸描いてちょんで新鮮なイメージですが味方キャラは従来の一般的なキャラクタでかわいいです。 しかも、ペーパー感が出るようにキャラクタを合わせているのも個人的に好きです。 最近のソシャゲは描く人で全然違ってきて個人的には統一感がなくてちょっと残念だったので。 それもそれでいいんですけどね。
とにかく僕のいい感じのところをつついてくれるゲームです。 遊び心あり、ゆるさあり、それにしっかりゲームになっている。 もう文句がないですな。 事前登録をなんとなくしてたんですがやって良かったと思います。
とりあえずこんな感じですかね。 まだあまりやっていないのでこの記事はこれまで。 また、なにか思ったら更新します!
【Unity】タイムゲージの作り方その1【小技集その1】
0. 今日作るもの
こんにちは、PlayWao!プログラム担当のかぼちゃです! 今日紹介するのは、 「タイムゲージの作り方」
タイムゲージは、下のように、時間によってゲージが減っていくもの。 もはや、おなじみですね。
ゲージそのものがゲーム画面から消えるわけではないので、ゲージそのものに細工する方針にしましょう。 画像の長さを変えたいとなると、TransformのScaleをいじる他なさそう。ということで、時間がたつ毎に、ゲージのScaleを縮めていく方針で行きましょう。
1. つくってみた
では、早速つくっていきます。 必要になるのは、ゲージの元になる長方形の画像のみ。
1) 画像の追加
まず元となるゲージの画像を用意して、Unityに追加します。 ドラッグ・アンド・ドロップして「Project」に放り込みましょう。
2) 画像の設定
「Project」に追加された画像を選択すると、「Inspector」に画像の設定を行う欄がでてきます。この画像の青い部分、「Pivot*」の部分をみてみると、値が{Center}となっています。
これをBottomに変えて、右下のApplyをクリックして保存します。
- *Pivotとは???
Pivotは、画像の基準点という意味です。正確にはTransformが参照する座標の位置のことです。TransformのPosition,Rotation,Scaleのすべてがこの座標を基準にして計算されます。具体的に見てみましょう。図中で左がPivot=Center、右がPivot = Bottomになっています。 まずはPositionから。以下は2つともY=0にした例
このように基準になる点をy=0にあわせようとします。もう想像付くかと思いますが、Rotationでは基準点を中心に回転され、Scaleも基準点を中心に伸縮が行われます。
伸縮の例を見てみるとわかりますが、タイムバーを作りたい場合、PivotをBottomにしたほうがいいですね!
3) 伸縮スクリプト作成
さていよいよ伸縮。画像を画面上に配置しましょう。画像(スプライト)を選択すると「Inspector」上に画像(スプライト)の詳細がでます。ここでAdd ComponentからNew Scriptを選んで、新しいスクリプトを作ります。
Inspectorでスクリプト名をダブルクリックするとエディタが開くので、以下のコードを打ち込みましょう。
void timerStart(){ float time = 5f; iTween.ScaleTo (this.gameObject,iTween.Hash("y",0f,"time",time)); }
スクリプトでは簡単のため、iTweenライブラリを使いました。(導入や使い方はココとかが詳しいです) ざっと説明すると、「このgameObject(タイムバーのこと)のy軸のScaleを5秒間で0にする」となっています。
4) 実行!!
さて、実行ボタンを押すと…見事5秒で縮むバーが! 今回は縦ですが、横にしたい場合は、PivotをLeftにして、xのScaleを変えましょう。また、例えば、値が減る毎に割合で縮むようにすれば、HPバーも作れますね。
2. 他の実装方法
上の実装は慣れていればものの数分であっという間にゲージが作れるでしょう。 実は、この方法簡単なのですが、デメリットもあります。それは 単純にScaleをいじるだけだと画像の比率がかわってしまうことです。
今回は無地のタイムゲージなので問題はありませんでしたが、上のような例だと、少し不格好です。
実は、これを回避するうまい方法があります。 それはカメラによるマスキングというもの・・・・ 次回はそのテクニックを紹介します。
では今回はこれにて!