PHPのslim3にtwigの設定を追加
前回TOPページのコントローラの追加を行ったので、今回はテンプレートの追加をします。
テンプレートにはtwigを使用します。
composer.jsonを更新してtwigのパッケージをインストールします
$ vi composer.json "require": { "php": ">=5.5.0", "slim/slim": "^3.1", "slim/twig-view": "2.2.0", $ php composer.phar update
rendererの設定にtwig用の項目を設けます。
$ vi src/settings.php // Renderer settings 'renderer' => [ 'template_path' => __DIR__ . '/../templates/', 'twig_settings' => [ 'cache' => __DIR__ . '/../cache', ], ],
コンテナのrendererにはphp-viewの設定がありますが、twigの設定に書き換えます。
$ vi src/dependencies.php $container['renderer'] = function ($c) { $settings = $c->get('settings')['renderer']; $view = new \Slim\Views\Twig($settings['template_path'], $settings['twig_settings']); $basePath = rtrim(str_ireplace('index.php', '', $c['request']->getUri()->getBasePath()), '/'); $view->addExtension(new \Slim\Views\TwigExtension($c['router'], $basePath)); return $view; };
コントローラでtwigのテンプレートを使用するように書き換えます。
public function index($request, $response, $args){ return $this->container['renderer']->render($response, 'top/index.twig', ['message' => '<test>']); }
twigテンプレートを追加します
$ vi templates/top/index.twig {{ message }}
これでtwig用の設定の追加は完了です
PHPのslim3で環境構築
久しぶりにslim3で素のフォームを作るまでを勉強しなおしたので筆をとりました。
この記事ではコントローラの追加までを記述します。
slimはマイクロフレームワークですが、公式でスケルトン機能を用意されているのでそれを利用します。
スケルトンをチェックアウト
$ php composer.phar create-project slim/slim-skeleton sample Installing slim/slim-skeleton (3.1.2) - Installing slim/slim-skeleton (3.1.2): Downloading (100%) ~(略)~ Writing lock file Generating autoload files $
すると以下のような感じのディレクトリ構成で環境が作成されます。
sample |-- CONTRIBUTING.md |-- Makefile |-- README.md |-- composer.json |-- composer.lock |-- composer.phar |-- logs |-- phpunit.xml |-- public | `-- index.php |-- src | |-- dependencies.php | |-- middleware.php | |-- routes.php | `-- settings.php |-- templates | `-- index.phtml |-- tests | `-- Functional `-- vendor
これでpublicディレクトリをドキュメントルートに設定すると簡単な画面が表示されます。
ルーティングにコントローラを追加する前にクラスのオートロード設定を追加します。
具体的にはcomposer.jsonに以下の記述を追加して更新します。
$ vi composer.json "autoload": { "psr-4": { "Sample\\": "src/Sample/" } }, $ php composer.phar update
ルーティングのファイルを編集します
$ vi src/routes.php <?php // Routes $app->get('/', \Sample\Controller\TopController::class . ':index');
コントローラのファイルを追加します
$ vi src/Sample/Controller/TopController.php <?php namespace Sample\Controller; use Psr\Container\ContainerInterface; class TopController { protected $container; public function __construct(ContainerInterface $container){ $this->container = $container; } public function index($request, $response, $args){ echo 'Hello World'; return $response; } }
これで再度トップページにアクセスすると「Hello World」と表示されるようになります。
pyramidのjinjaテンプレートで独自filterを定義
pyramidのテンプレートで「nl2br」のような独自フィルターを挟みたいと思った時にメモです。
今回は「hoge2hage」という「hoge」を「hage」に変換するフィルターを定義してみます。
1. フィルター処理を作成
適当なところに処理を作成します。
$ vi myapp/filters.py def hage2hoge(value): return re.sub(r'hage', 'hoge', value)
2. フィルターを定義
定義をコンフィグファイルに記載します。
$ vi development.ini [app:main] use = egg:myapp pyramid.reload_templates = true pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar jinja2.filters = hoge2hage = myapp.filters:hoge2hage
3. テンプレートでフィルターを使用
あとは以下のように使用すればjinjaテンプレートのフィルターとして動作します。
$ vi myapp/templates/hello.jinja2 hello {{data|hoge2hage}}
pyramidでpyramid-deformを使ってみる
前回の記事で簡単なdeform利用はできるようになりました。
pyramidにはpyramid-deformと呼ばれるdeform連携パッケージが存在するので今回はpyramid-deformを利用してのdeform利用を試します。
1. リソースの定義
自分はpython歴が浅いのでpython界で常道なのかは知らないですが、参考にしたサイトの書き方を見る限りpyramidはリソースという関数を定義してそこでリクエストパラメータをrequestオブジェクトに割り当てる事ができるようです。
add_routeの第三引数で定義したform_indexの戻り値がrequestオブジェクトのcontextに代入されます。
// 「/form」へリクエスト発生すると動作する処理 $ vi myform/resources.py def form_index(request): values = { 'name' : '', 'shoe_size' : 0 } if 'name' in request.POST: values['name'] = request.POST['name'] if 'shoe_size' in request.POST: values['shoe_size'] = request.POST['shoe_size'] return values // 上記で定義した「form_index」をadd_routeで関連付 $ vi myform/__init__.py from pyramid.config import Configurator from myform.resources import form_index def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(settings=settings) config.include('pyramid_jinja2') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('form_index', '/form', factory=form_index) config.scan() return config.make_wsgi_app()
2 viewの変更
前回作成したviewを以下のように修正します。
・クラスにルーティング設定のデコレータを移動
・save_successにPOSTが成功した場合の処理を定義
・appstructにリクエストデータをスキーマクラスに紐づけする処理を定義
$ vi myform/views.py from pyramid.view import view_config from pyramid_deform import FormView from myform.schema import Person @view_config(route_name='form_index', renderer="templates/form.jinja2") class PageEditView(FormView): schema = Person() buttons = ('save',) form_options = (('formid', 'pyramid-deform'), ('method', 'POST')) def save_success(self, appstruct): context = self.request.context context['name'] = appstruct['name'] context['shoe_size'] = appstruct['shoe_size'] return None def appstruct(self): context = self.request.context return {'name': context['name'], 'shoe_size': context['shoe_size']}
3 templateの変更
contextでデータにアクセスするように修正します。
<html> <head> <title>Projector</title> </head> <body> <h2>Hello Form!</h2> <div >{{ form| safe }}</div> <p>Valid form values: {{context.name}} and {{context.shoe_size}}.</p> </body> </html>
あとはdeformの使い方を調べればある程度のpyramidでのdeformマスターにはなれると思われます。
pyramidでdeformを使ってみる
以前の記事でmongoengineの使い方が解ってきました。今度はdeformを使ってのフォームの作成を試してみました。
最終的にはpyramid-deformを利用したフォーム連携を目指しますが、まずはdeformを素で使ったシンプルなフォームを作成してからpyramid-deformを利用に進みます。
1. deformのインストール
pyramid-deformをインストールします。deform、colanderも一緒にインストールされます。
$ pip install pyramid-deform
2. schemaの作成
フォームの構造はスキーマと呼ばれるクラスで定義します。
今回の例ではnameという文字列型の項目とshoe_sizeという数値型の項目を定義してます。
$ vi myform/schema.py import colander class Person(colander.MappingSchema): name = colander.SchemaNode(colander.String()) shoe_size = colander.SchemaNode( colander.Integer(), missing = 0, )
3. viewの作成
「/form」でアクセス可能なviewを用意します。
$ vi myform/__init__.py from pyramid.config import Configurator def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(settings=settings) config.include('pyramid_jinja2') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('form_index', '/form') config.scan() return config.make_wsgi_app() $ vi myform/views.py from pyramid.view import view_config from deform import Form from deform import ValidationFailure from myform.schema import Person class ProjectorViews(object): def __init__(self, request): self.request = request @view_config(route_name='form_index', renderer="templates/form.jinja2") def site_view(self): schema = Person() myform = Form(schema, buttons=('submit',)) if 'submit' in self.request.POST: controls = self.request.POST.items() try: appstruct = myform.validate(controls) except ValidationFailure, e: return {'form':e.render(), 'values': False} values = { "name": appstruct['name'], "shoe_size": appstruct['shoe_size'], } return {"form": myform.render(), "values": values} return {"form": myform.render(), "values": None}
4. templateの作成
テンプレートではPOSTされたら、その値をフォームの下に表示してます。
$ vi myform/templates/form.jinja2 <html> <head> <title>Projector</title> </head> <body> <h2>Hello Form!</h2> <div >{{ form| safe }}</div> <p>Valid form values: {{values.name}} and {{values.shoe_size}}.</p> </body> </html>
5. 確認
サーバを起動してこんな画面が表示されたらまずはdeform理解の第一歩に成功です。
PyCon APAC 2013に行ってきた(2日目)
前日朝までFF14をやってましたが、エオルゼアのご加護のおかげでなんとか遅刻せずに2日目に参加できました。
PofEAA in SQLAlchemy
資料はなかったので動画が以下です。
http://www.youtube.com/watch?v=nClPtRhlDxs
SQLAlchemyと関連してPofEAAに記載されているパターン紹介とデータマッピング周りの発表者さんの考える良い設計的な内容でした。
ユニットオブワーク、一意キーマッピング・・と主にデータ設計周りのパターンの紹介でしたが、現在の自分の興味領域と被っていたので理解は出来た方だと思います!後半の発表者さんの設計についての話は、なんとなくですがドメイン駆動設計の本を読むとサービス/レポジトリ/エンティティみたいな概念がフィットするかもしれないなと思いました。
途中アクティブレコードモデルの肥大化の問題点みたいな話をされていたので、だったらデータマッパーの方が良い書き方ができるのでは的な意図でSQLAlchemyでデータマッパーなアーキテクチャは使えるのかと質問しましたが、最初の例のプログラムがデータマッパー的な書き方をしてたのでそもそもSQLAlchemyはデータマッパーである事が自明であり愚問でした、すいません!
Webセキュアコーディングの基本
資料は以下です。
http://www.slideshare.net/gjo/pycon-apac-2013-web-secure-coding
pythonのセキュリティ話というよりもセキュリティを保つためにはどんな感じにやってけば良いのかみたいな内容でした。
個々の処理にescape処理をいれるよりもフレームワークなど仕組みとしてエスケープすべしなど意識せずに実施しているような基礎を思い出させてくれました。
pythonで、ハードウェアをWebAPIにする話
資料はなかったので動画が以下です。
http://www.youtube.com/watch?v=KwmU3dRZagY
会社ではなく趣味的?な開発チームをやってる人がarduinoとRaspberry Pi(Linux)を連携をweb apiで実装したよって内容でした。
arduinoの温度センサからシリアル接続で温度を取得してウエブで表示するデモ等ありました。pythonだとCのラッパーが書きやすいという事なので、シリアル接続などC言語がはびこる領域との連携はpythonだと実装しやすいんだろうなという印象をうけました。
Django 1.5 における効果的な MTV 設計 & ネイティブApp
資料は以下です。
http://www.slideshare.net/luyikei
前半は設計の話ではなくDjangoでのmodel、template、session、cache等の機能のtips、後半はPyQtを利用したネイティブアプリの実装例の紹介という内容でした。紹介文で「日本 Qt ユーザー会を設立。日本における Qt の情報の発信を促進する傍ら、 Django でWEBアプリケーションを作成することをメインとして活動する。」のように書いてあったのでギークなおっさん的な人を想像したのですが普通に高校生でした。自分が高校生の時はwindows95で自作ブームだったなぁとノスタルジックな気持ちで生暖かい気持ちで聞いたセッションです。
Django最速デバッグ指南
資料は以下です。
http://www.slideshare.net/hirokiky/django-yconapac2013
デバッグツール(django-debug-toolbar/django-pdb/django-devserver)の紹介とロギングの説明な内容でした。
発表者の人が発表慣れをしている感じで説明がわかりやすかったです。実際の画面を操作しながら説明してくれたのでpython弱者の自分がこのセッションを聞いただけであたかもデバッグ力が上がったかのような感覚を与えてくれましたw
後半はログの話でerror,warning,info,debugの扱いでprintデバッグするならdebugログだそうよって話では自分は適当に出しがちなので意識の低さに自省しました。
LT
英語でのLTだったのであまり理解できませんでした(汗
感想
個人的にはじめてのPyCon参加だったのですが、発表内容に関していうと他言語のカンファレンスとそんなに違いは無い印象をうけました。ただ、pythonは海外の方が盛んなので英語のセッションが半分を占めるのは他言語ではない所かなと思いました。
pyramidでmongoengineを使ってみる
pythonのmongodb用ODM(ORM)であるmongoengineのお話です。
mongoengineでmongoのデータを更新するフォームの勉強をしたかったので、mongoengineを軽く触ってみました。
1. 環境構築
今回のテスト用にmyform環境を作成し「/form」をルーティングに追加します。
ちなみにSymfony2でtwigを使ってたのでテンプレートエンジンには書式が同じjinja2を設定してます。
// 必要モジュールのインストール $ pip install pyramid==1.3.4 $ pip install waitress $ pip install pyramid_jinja2 $ pip install mongoengine // プロジェクト作成 $ pcreate -s starter myform $ cd myform $ python setup.py develop // 「/form」をルーティングにしてテンプレートエンジンにjinja2を設定 $ vi myform/__init__.py from pyramid.config import Configurator from mongoengine import connect def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(settings=settings) config.include('pyramid_jinja2') config.add_static_view('static', 'static', cache_max_age=3600) config.add_route('form_index', '/form') config.scan() connect('pyramid_form') return config.make_wsgi_app()
2. コレクション用のモデルを作成
「email」「first_name」「last_name」の文字列型のカラムを持った「user」コレクションのモデルを定義します。
$ mkdir myform/models $ touch myform/models/__init__.py $ vi myform/models/user.py from mongoengine import Document from mongoengine import StringField class User(Document): email = StringField(required=True) first_name = StringField(max_length=50) last_name = StringField(max_length=50)
3. データを挿入するviewを作成
Userモデルのインスタンスを生成して保存するビューとテンプレートを作成
// データを挿入するview $ vi myform/views.py from pyramid.view import view_config from models.user import User @view_config(route_name='form_index', renderer='templates/form/index.jinja2') def my_view(request): test = User(email='test1@example.com') test.first_name = 'test1_fname' test.last_name = 'test1_lname' test.save() return {'value':'test!'} // テンプレート作成 $ mkdir myform/templates/form/ $ vi myform/templates/form/index.jinja2 hello {{ value }}
4. サーバを起動して確認
サーバを起動後「/form」にアクセスすると「hello test!」と表示されます。
mongodbにデータが保存されている事が確認できます。
$ mongo MongoDB shell version: 2.4.5 connecting to: test > show dbs; local 0.078125GB pyramid_form 0.203125GB > show collections system.indexes user > var x = db.user.findOne(); > printjson(x) { "_id" : ObjectId("52345cfe2e5229637bcaa389"), "email" : "test1@example.com", "first_name" : "test1_fname", "last_name" : "test1_lname" }
5. EmbeddedDocumentの使用
mongodbの特性の1つにEmbeddedDocument機能があるので使ってみました。
「post」コレクションにCommentという埋め込みドキュメントを設定します。
$ vi myform/models/posts.py # --* encoding:utf-8 *-- from mongoengine import Document from mongoengine import EmbeddedDocument from mongoengine import StringField from mongoengine import ReferenceField from mongoengine import ListField from mongoengine import EmbeddedDocumentField from .user import User class Comment(EmbeddedDocument): content = StringField() name = StringField(max_length=120) class Post(Document): title = StringField(max_length=120, required=True) author = ReferenceField(User) tags = ListField(StringField(max_length=30)) comments = ListField(EmbeddedDocumentField(Comment)) meta = {'allow_inheritance': True} class TextPost(Post): content = StringField() class ImagePost(Post): image_path = StringField() class LinkPost(Post): link_url = StringField() // TextPostとLinkPostの2つのインスタンスを作成して保存 $ vi myform/views.py from pyramid.view import view_config from models.user import User from models.posts import TextPost from models.posts import LinkPost from models.posts import Comment @view_config(route_name='form_index', renderer='templates/form/index.jinja2') def my_view(request): user = User(email='test2@example.com', first_name='test2_fname', last_name='test2_lname').save() comment = Comment(content='comment_content', name='comment_name') post1 = TextPost(title='Fun with MongoEngine', author=user) post1.content = 'Took a look at MongoEngine today, looks pretty cool.' post1.tags = ['mongodb', 'mongoengine'] post1.comments = [comment] post1.save() post2 = LinkPost(title='MongoEngine Documentation', author=user) post2.link_url = 'http://docs.mongoengine.com/' post2.tags = ['mongoengine'] post2.save() return {'value':'test!'}
6. EmbeddedDocumentの確認
mongoで確認するとauthorでUserとの関連が設定されて、commentsにコメントが埋め込まれている事が確認できます。
> db.post.find().forEach(printjson); { "_id" : ObjectId("52345fda2e52296395f90bdf"), "_cls" : "Post.TextPost", "title" : "Fun with MongoEngine", "author" : ObjectId("52345fda2e52296395f90bde"), "tags" : [ "mongodb", "mongoengine" ], "comments" : [ { "content" : "comment_content", "name" : "comment_name" } ], "content" : "Took a look at MongoEngine today, looks pretty cool." } { "_id" : ObjectId("52345fda2e52296395f90be0"), "_cls" : "Post.LinkPost", "title" : "MongoEngine Documentation", "author" : ObjectId("52345fda2e52296395f90bde"), "tags" : [ "mongoengine" ], "comments" : [ ], "link_url" : "http://docs.mongoengine.com/" }
さすがODMって感じです。
参考