2014年4月30日水曜日

Pyramid 1.5 + jinja2-alchemy-starter で @view_config()

Pyramid 1.5 で、jinja2-alchemy-starter で作成したプロジェクトを修正します。
pyramid のプロジェクトをつくるときに bin/pcreate -s starter MyProject とか、bin/pcreate -s alchemy MyProject とかするわけですが、どうも jinja2 とうまくいかないことがあります。
そこで jinja2-alchemy-starter  です。
別途インストールする必要がありますが、これでプロジェクトをつくるとデフォで jinja2 になっていて、しかも alchemy 付きになってます。
これは便利なので、これからは jinja2-alchemy-starter  を使おうと思います。

で、今回は前回の jinja2-alchemy-starter でつくったプロジェクトをちょっと修正して、簡単なサンプルをつくってみます。

二つのページをつくって、行ったり来たりできるようにしてみます。テンプレートはひとつにします。
テンプレートの拡張子を .jinja2 だけではなく、.html も使えるようにします。
__init__.py の config.add_view() ではなく、@view_config() を使うように修正します。
  1. py ソースの頭にこれを追加する
    #!/usr/bin/env python
    # coding: UTF-8
    

  2. myproject/__init__.py を修正する
    <修正前>
        config.include('pyramid_jinja2')
        
        #The views/routes are added here
        config.add_static_view('static', 'static')
        
        config.add_route("my_route",'/')
        config.add_view('myproject.views.my_view',
                        route_name="my_route",renderer="mytemplate.jinja2")
        
        return config.make_wsgi_app()
    

    <修正後>
        config.include('pyramid_jinja2')
        config.add_renderer(".html", "pyramid_jinja2.renderer_factory")
    
        #The views/routes are added here
        config.add_static_view('static', 'static')
        
        config.add_route('home', '/')
        config.add_route('next_page', '/next')
        config.scan()
        
        return config.make_wsgi_app()
    

    何を修正したかというと・・・
    config.add_renderer() を追加しました。
    これは、テンプレートのファイル名の拡張子を .jinja2 だけではなく、.html も使えるようにしています。
    config.add_route() で、'/' は views.home()、'/next' で views.next_page() を関連づけました。
     config.scan() は、「@view_config()」を調べて、config.add() してくれます。
    URI とテンプレートファイル名をここで対応づけていましたが、テンプレートファイル名は views.py のその関数のところで対応づけるようになりました。

  3. views.py を修正する。
    インポートの追加。@view_config() を使えるようにします。
    from pyramid.view import view_config
    

    それぞれのページの処理を追加します。最初にあった my_view() は削除します。
    <修正前>
    _ = TranslationStringFactory('MyProject')
    
    def my_view(request):
        session = DBSession()
        #Use session to make queries
        #session.query()
        return {'project':'MyProject'}
    

    <修正後>
    _ = TranslationStringFactory('MyProject')
    
    @view_config(route_name='home', renderer='home.html')
    def home(request):
        page_name = 'home'
        return {'page_name':page_name, 'next_uri':'/next', 'next_name':'next_page'}
    
    @view_config(route_name='next_page', renderer='home.html')
    def next_page(request):
        page_name = 'next_page'
        return {'page_name':page_name, 'next_uri':'/', 'next_name':'home'}
    

  4. テンプレートファイル home.html を追加する
    head は title だけでも良いです。
    <a href= の {{ request.application_url }} はなくても動きます。
    <html><!-- home.html -->
    <head>
        <title>{{ page_name }}</title>
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
        <link rel="shortcut icon" href="{{request.application_url}}/static/favicon.ico" />
        <link rel="stylesheet" href="{{request.application_url}}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
    </head>
    <body>
      This page name is '{{ page_name }}'.<br />
      <a href="{{ request.application_url }}{{ next_uri }}">{{ next_name }}</a> 
    </body>
    </html>
    

次回は、せっかくの alchemy なので、MySQL とつないで、ログインが必要なページをつくってみます。

Pyramid 1.5 + jinja2-alchemy-starter で pcreate

Pyramid 1.5 で、Jinja2 をやっていたが、なにやらテンプレートが見つからない、などというエラーが時々でてしまう。
Mac じゃ動いているのに、Raspberry Pi ではテンプレートが見つからないとか。
いろいろやってみて見つけたのが「jinja2-alchemy-starter」。
これは「Pyramid Scaffold for getting started with SQLAlchemy ORM and Jinja2 Templating Engine」というもの。
さしあたって、Pyramid 1.5 に Jinja2 で良いのだけれど、現実のお仕事は SQLAlchemy も使うので、これは便利じゃないのか、と早速試してみる。
これはその記録。

env_jinja2 という環境を virtualenv でつくって、そこでプロジェクト MyProject を作成する。

$ virtualenv --no-site-packages env_jinja2
$ cd env_jinja2/
$ bin/pip install pyramid
$ bin/pip install jinja2_alchemy_starter
$ bin/pcreate -s jinja2_alchemy_starter MyProject

これでプロジェクト MyProject ができた。
ついでに、eclipse で作業できるようにしてしまう。
eclipse を起動して、workspace を env_jinja2 にする。
起動したら、環境設定を修正する。PyDev がインストールされている eclipse であることを前提にしてますから、インストールしていない方は、インストールしておいてください。
  1. General -> Editors -> Text Editors の Insert spaces for tabs にチェックを入れる。tab コードの代わりにスペースを使う。(tab コードがお好きな方はそのままで)
  2. General -> Startup and Shutdown  で、RSE UI のチェックを外す。これは、意味不明の「RemoteSystemsTempFiles」を eclipse が勝手に作成するのをやめさせるため。
  3. General -> Workspace の Text file encoding を UTF-8 にする。(他の encoding が必要な場合は、それを設定してください)
  4. Remote Systems の Re-open Remote Sysmtes ... のチェックを外す。これも、意味不明の「RemoteSystemsTempFiles」を eclipse が勝手に作成するのをやめさせるため。
  5. PyDev -> Interpreters -> Python Interpreter で、自分の環境の Python にする。(env_jinja2/bin/python です)
  6. 続いて表示される PYTHONPATH の設定は、めんどうなので、すべてチェックを入れておく。(きちんと管理したい方はそのように)
  7. PyDev -> Interpreters -> Python Interpreter の String Substitution Variablesに名前が「run_pyramid」で、値が自分の環境にあるbin/pserveを追加する。

次に、先ほど作成した MyProject を eclipse のプロジェクトにしたいので、その名前で新たに Project を作成します。
Project name は MyProject にします。
つくるときに、Interpreter を先ほど設定した Python Interpreter にします。
eclipse の PyDev Package Explorer に MyProject が表示されるので、その中から development.ini を見つけて編集状態にします。
このままでは、MySQL とつながりにいってしまうので、その前に sqlite を使うようにします。
<修正前>
#SQlalchemy configuration (The prefix `sqlalchemy.` can be changed IF you change it in __init__.py's engine_from_config() call too
sqlalchemy.url=mysql+mysqldb://root:root123@localhost/test
sqlalchemy.pool_recycle = 3600
sqlalchemy.pool_size = 20
<修正後>
#SQlalchemy configuration (The prefix `sqlalchemy.` can be changed IF you change it in __init__.py's engine_from_config() call too
# sqlalchemy.url=mysql+mysqldb://root:root123@localhost/test
# sqlalchemy.pool_recycle = 3600
# sqlalchemy.pool_size = 20
sqlalchemy.url = sqlite:///%(here)s/MyProject.sqlite

次に MyProject を初期化して、pserve で起動してみる。
$ cd MyProject/
$ ../bin/python setup.py develop
$ ../bin/pserve development.ini 

「ImportError: No module named MySQLdb」って、言われたときは、先ほどの development.ini の修正を確認してください。

このままではなんだかわからないので、テンプレートを使って、ページ遷移もしてみます。
__init__.py を見ると「config.add_view」を使っているので、この辺りも修正します。

2014年4月18日金曜日

Pyramid 1.5 mako -> jinja2

Python Pyramid 1.4 を 1.5 にしたときのメモ。
テンプレートの扱いが変わってしまったので、ソースを修正しないと動かなくなってしまった。
テンプレートは mako を使用中。
環境についてはこう。
Mac OS X 10.9.2
Python 2.7.6

Pyramid については、ここを参照。

  1. development.ini
    テンプレートファイルが入っているディレクトリの設定はそのまま。
    mako.directories = my_project:templates

  2. setup.py
    ここは、requires に 'pyramid_mako' を追加する。
    requires = [
        'pyramid_mako',
        'pyramid',
    

  3. my_project/__init__.py
    config = Configurator(settings=settings, の後にこれを追加。
    config.include('pyramid_mako')
    

    テンプレートファイルの拡張子に .html を追加するところは、こう修正した。
    これがなかなかわからなかった。
    <修正前>
    config.add_renderer(".html", "pyramid.mako_templating.renderer_factory")
    
    <修正後>
    config.add_mako_renderer('.html', settings_prefix='mako.')
    

  4. views.py の renderer の設定は前のまま
    そのまま「renderer='home.html'」などとすることができる。
    @view_config(route_name='home', renderer='home.html', permission='edit', request_method="GET")
    

これで動くはず。
mako もいいけど jinja2 にしたい、という場合は「config.add_renderer()」のところと「mako.directories = pypwm:templates」をどうしたらよいのでしょう。
ついでにちょっとやってみる。

  1. development.ini
    pyramid.includes に pyramid_jinja2 を追加する。
    それと、jinja2.directories の設定。
    pyramid.includes =
        pyramid_jinja2
    #    pyramid_debugtoolbar
        pyramid_tm
    
    jinja2.directories = my_project:templates
    

  2. setup.py
    requires = [
        'pyramid_jinja2',
        'pyramid',
    

  3. my_project/__init__.py
    config.add_jinja2_search_path も追加する。
    config.include('pyramid_jinja2')
    config.add_renderer(".html", "pyramid_jinja2.renderer_factory")
    config.add_jinja2_search_path("pypwm:templates")
    


こんな感じでした。
mako テンプレートを jinja2 に書き換える方がたいへんです。

mako jinja2
コメント
## for maco
<%doc>multi line</%doc>
{# for jinja2 #}
{# multi line #}
include
<%include file="my_header.html"/>
{% include "my_header.html" %}
request.
static_url
など
"${request.static_url(
'pypwm:static/my.css')}"

${request.route_url('home')}
{{ request.application_url }}
/static/my.css"

{{ request.application_url }}/home
for loop
% for item in items:
% endfor
{% for item in items -%}
{%- endfor %}
if
% if xxx == 'yyy':
% else:
% endif
{% if xxx' == 'yyy' %}
{% else %}
{% endif %}
変数
${my_var}
{{ my_var }}