PHPのslim3で環境構築

久しぶりにslim3で素のフォームを作るまでを勉強しなおしたので筆をとりました。
この記事ではコントローラの追加までを記述します。

slimはマイクロフレームワークですが、公式でスケルトン機能を用意されているのでそれを利用します。

github.com

ケルトンをチェックアウト

$ 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ディレクトリをドキュメントルートに設定すると簡単な画面が表示されます。

f:id:taka512:20170401231509p:plain
ルーティングにコントローラを追加する前にクラスのオートロード設定を追加します。

具体的には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理解の第一歩に成功です。

f:id:taka512:20130928194035p:plain

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って感じです。

参考

http://docs.mongoengine.org/en/latest/tutorial.html

PyCon APAC 2013に行ってきた(1日目)

最近、仕事でpythonに触れる事が多いのでPyCon APAC 2013にいってきました。1日目に聞いたセッションの感想です。

パッケージングの今と未来

資料は以下です。
http://www.slideshare.net/aodag/ss-26183017

自分は7月から本格的にpythonを触り始めたのでpythonの歴史とか常識など抜けてる部分が結構あってその辺の知識の補完を期待して聞きました。
内容としてはわりと期待通りでパッケージングの基礎〜今〜未来とどんなツールが使われてきて、モダンと言われる構成の紹介と共に今後はこんなツールがあるよ的な内容でした。pythonに深く関わっている人の生の声的な事を聞けたのはよかったです。

sphinxと僕

資料は以下です
https://dl.dropboxusercontent.com/u/540566/pyconapac2013/_build/html/index.html

発表者はtell-kさん・・尊敬する大先輩ですね。昔はこのような大舞台で発表する事はなかったので大きくなったなと目頭が熱くなりました(上から目線)
前半はsphinxの使い方、後半はsphinxで出来る事を語っておられました。始める前からスライドの時間を気にしていましたがきっちり終わらすのはさすがです。ただ、最初から気にしてるなら内容を削っても良かったのではと思いました!

Python ユーザのための構成管理入門

資料は以下です。
http://www.slideshare.net/TakeshiKomiya/python-26185953

fabricやcapistrano的なアプリの構成管理を期待して聞いたのですが、chefやpuppetのようなサーバ構成管理のお話でしたw
冪等性などサーバの構成管理の概念とchefの使い方のお話で、中身も勉強になりましたが発表者さんの話が淀みなかった上、スライド内容につながりがあって発表スキルが高さが個人的に一番気になりました!

PythonとDataDogを使って簡単システムモニターリング

スライドは見つからなかったですが、動画が下記にありました。
http://www.youtube.com/watch?v=ow5kreNQaHA&feature=youtu.be&a

zabbix的な監視ツールのDataDogの紹介でした。個人的には便利そうで使ってみたいと思ったけど、仕事で使う時にはデータを取る時にアプリケーションに入れる処理が大量アクセス時にも大丈夫かなとか少し現実的な事を考えてしまいました。モニタリングでのfacebookって言葉が一番印象に残りました。

Programming AWS with Python

資料は以下です。
http://www.slideshare.net/understeer/20130914-py-conapacawspublic

今日一番自分に刺さったセッションだと思います。awsのpython SDK(boto)を使ったawsツール開発のお話で、元々非公式sdkだったのが開発者がawsの中の人になってそのまま公式化したらしく、対応機能の種類や新機能への対応速度はjavaと並んで早いとの事。有用な話が聞けたのでJAWS FESTA Kansai 2013の期待値も高まりました。行こうかな・・

about mock

資料は以下です。
http://podhmo.github.io/pycon/slide/index.html?transition=none#/

mockで必要とされるような事を自分で実装してみた!みたいなお話だったと思うのですが、理解が追いつけなくて自分のpython力の低さを実感しました。

明日も楽しみです!