基本的なレイアウト

alchemy scaffold によって生成される初期ファイルは非常に基本的なもの ですが、ほとんどの url dispatch ベースの Pyramid プロジェクト に共通する高レベルパターンの良い方向性を提供します。

このチュートリアルステージのソースコードを以下の場所で閲覧することができます。 http://github.com/Pylons/pyramid/tree/1.3-branch/docs/tutorials/wiki2/src/basiclayout/.

__init__.py でのアプリケーション設定

ディスク上のディレクトリは __init__.py ファイルを含むことによって Python package になることができます。このファイルがたとえ空でも、 Python パッケージとしてディレクトリをマークします。私たちはディレクトリ がパッケージであることを示すマーカーとして、および設定コードを置くために __init__.py を使用します。

tutorial/tutorial/__init__.py を開いてください。 すでに以下の内容が含まれているはずです:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from pyramid.config import Configurator
from sqlalchemy import engine_from_config

from .models import (
    DBSession,
    Base,
    )


def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    config = Configurator(settings=settings)
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.add_route('home', '/')
    config.scan()
    return config.make_wsgi_app()

順番に見て行きましょう。 まず、後のコードのためにいくつかのインポートが必要です:

1
2
3
4
5
6
7
from pyramid.config import Configurator
from sqlalchemy import engine_from_config

from .models import (
    DBSession,
    Base,
    )

__init__.pymain という名前の関数を定義しています。 これは __init__.py の中で定義された main 関数全体です:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def main(global_config, **settings):
    """ This function returns a Pyramid WSGI application.
    """
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)
    Base.metadata.bind = engine
    config = Configurator(settings=settings)
    config.add_static_view('static', 'static', cache_max_age=3600)
    config.add_route('home', '/')
    config.scan()
    return config.make_wsgi_app()

pserve development.ini コマンドを起動すると、上記の main 関数が 実行されます。 main 関数は、いくつかの設定を受け取って WSGI アプリケーションを返します (pserve に関する詳細は Startup を参照してください)。

main 関数は最初に engine_from_config を使用して development.ini ファイルの [app:main] セクション中の sqlalchemy. 接頭辞のついた 設定から SQLAlchemy データベースエンジンを作成します。これは (sqlite:// のような) URI になります:

    engine = engine_from_config(settings, 'sqlalchemy.')

main はその後、 engine を渡すことで SQLAlchemy セッションオブジェクト を初期化します:

    DBSession.configure(bind=engine)

main は続いて SQLAlchemy の declarative (宣言的) Base オブジェクトを 初期化します (作成した engine を metadata オブジェクトの bind 属性に 割り当てます)。これは、 (クラス構文によって宣言的に行う代わりに) 命令的に 行われたテーブル定義が動作するようにします。このアプリケーションの中では そのようなテーブルを使用しませんが、後で (それもこのチュートリアルのこと なんか忘れてしまうくらいかなり後になって) そのようなテーブルを追加したなら、 それが動作しないことにずっと悩むことになるでしょう。

    Base.metadata.bind = engine

main の次のステップは Configurator オブジェクトを構築することです:

    config = Configurator(settings=settings)

settings**settings 引数として渡された辞書の値を持つ キーワード引数として Configurator に渡されます。 これは .ini ファイルからパースされた設定の辞書になり、これには デプロイ関連の値である pyramid.reload_templatesdb_string などが含まれます。

main は 2つの引数、 static (名前) と static (パス) を引数と して pyramid.config.Configurator.add_static_view() を呼び出します。

    config.add_static_view('static', 'static', cache_max_age=3600)

これは /static 接頭辞から始まる全ての URL に一致する静的リソースの ビューを登録します (add_static_view への最初の引数によって)。これによって tutorial パッケージの中の static ディレクトリにある静的リソースが、 この場合は http://localhost:6543/static/ 以下を経由して返されるように なります (add_static_view への2番目の引数によって)。この宣言によって、 /static から始まる全ての URL は静的ビューに行かなくてはならない、と いうことを表しています。パスの残りの全ての部分 (例: /static/foo/foo) は CSS ファイルなどの静的ファイルリソースへのパスを作成するのに使われます。

また、 main は configurator を使用して URL が / の場合に使用される route configurationpyramid.config.Configurator.add_route() メソッド経由で登録します:

    config.add_route('home', '/')

このルートは / に一致する パターン を持っているので、 URL / 、 例えば http://localhost:6543/ を閲覧した場合にマッチします。

main は、次に configurator の scan メソッド (pyramid.config.Configurator.scan()) を呼び出します。 これは @view_config (また他の特別な) デコレータを探して tutorial パッケージを再帰的に走査します。 @view_config デコレータが見つかったら、 ビュー設定が登録されます。それはアプリケーション URL の 1 つをあるコード にマップすることを可能にするでしょう。

    config.scan()

最後に、 main は設定を終えて、 pyramid.config.Configurator.make_wsgi_app() メソッドを使用して WSGI アプリケーションを返します。

    return config.make_wsgi_app()

views.py によるビュー定義

route からそのルートのパターンが一致する時に実行されるコードへ のマッピングは view configuration の登録により行われます。 このアプリケーションでは、各ルートにビュー callable をマッピングするために pyramid.view.view_config() デコレータを使用していて、それによって URL パターンをコードにマッピングします。

tutorial/tutorial/views.py を開いてください。 すでに以下の内容が含まれているはずです:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from pyramid.response import Response
from pyramid.view import view_config

from sqlalchemy.exc import DBAPIError

from .models import (
    DBSession,
    MyModel,
    )


@view_config(route_name='home', renderer='templates/mytemplate.pt')
def my_view(request):
    try:
        one = DBSession.query(MyModel).filter(MyModel.name == 'one').first()
    except DBAPIError:
        return Response(conn_err_msg, content_type='text/plain', status_int=500)
    return {'one': one, 'project': 'tutorial'}

conn_err_msg = """\
Pyramid is having a problem using your SQL database.  The problem
might be caused by one of the following things:

1.  You may need to run the "initialize_tutorial_db" script
    to initialize your database tables.  Check your virtual 
    environment's "bin" directory for this script and try to run it.

2.  Your database server may not be running.  Check that the
    database server referred to by the "sqlalchemy.url" setting in
    your "development.ini" file is running.

After you fix the problem, please restart the Pyramid application to
try it again.
"""

ここで重要なのは、 @view_config デコレータがそれがデコレートする関数 (my_view) を以下のような view configuration と関連付けるという ことです:

  • route_name (home)
  • renderer これはパッケージの templates サブディレクトリに存 在するテンプレートです。

home ビューに関連したパターンがリクエストの間にマッチした場合、 my_view() が実行されるようになります。 my_view() は辞書を返します; レンダラーは辞書中の値に基づいてレスポンスを生成するために templates/mytemplate.pt テンプレートを使用します。

my_view()request という名前の単一の引数を受け取ることに注意 してください。これは Pyramid view callable の標準的な呼び出し シグネチャです。

__init__.py の中で pyramid.config.Configurator.scan() メソッド (つまり config.scan()) を実行した時のことを覚えていますか? scan メソッドを呼ぶ目的は、アプリケーション内のビュー設定を生成する ために、この @view_config デコレータを見つけて処理することでした。 scan によって処理されなければ、デコレータは実質的に何もしません。 scan によって検知されなければ、 @view_config は不活発です。

scaffold によって作成されたサンプルの my_view() は、 try:except: 節を使用して、プロジェクトデータベースへのアクセスに問題が あるかどうかを検出し、代替エラーレスポンスを提供します。そのレスポンスは、 ファイルの最後に示されたテキストを含み、問題を解決するためにユーザが 取ることのできるアクションについて通知するためブラウザに表示されます。

models.py とコンテンツのモデル

SQLAlchemy ベースのアプリケーションの中で、 model オブジェクトは SQL データベースに問い合わせることによって構成されるオブジェクトです。 alchemy scaffold は、モデルを実装するクラスを models.py ファイルに 出力します。

tutorial/tutorial/models.py を開いてください。 すでに以下の内容が含まれているはずです:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from sqlalchemy import (
    Column,
    Integer,
    Text,
    )

from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.orm import (
    scoped_session,
    sessionmaker,
    )

from zope.sqlalchemy import ZopeTransactionExtension

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()


class MyModel(Base):
    __tablename__ = 'models'
    id = Column(Integer, primary_key=True)
    name = Column(Text, unique=True)
    value = Column(Integer)

    def __init__(self, name, value):
        self.name = name
        self.value = value

それでは詳しく見てみましょう。最初に、この後のコードを動かすために いくつかのインポートが必要です:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
from sqlalchemy import (
    Column,
    Integer,
    Text,
    )

from sqlalchemy.ext.declarative import declarative_base

from sqlalchemy.orm import (
    scoped_session,
    sessionmaker,
    )

from zope.sqlalchemy import ZopeTransactionExtension

次に SQLAlchemy の DBSession オブジェクトをセットアップします:

DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))

scoped_sessionsessionmaker は SQLAlchemy の標準的なヘルパー です。 scoped_session は、データベース接続にグローバルにアクセスできる ようにします。 sessionmaker はデータベースのセッションオブジェクトを 作成します。システムが自動的にデータベーストランザクションを管理する ことを可能にするために sessionmakerextension=ZopeTransactionExtension() 拡張オプションを渡しています。 ZopeTransactionExtension を有効にすると、アプリケーションはすべての リクエストの後で自動的にトランザクションのコミットを発行します。ただし、 例外が上げられた場合にはトランザクションは abort します。

また、モデルのベースクラスとして使うために declarative Base オブジェクトを作成する必要があります:

Base = declarative_base()

モデルクラスはこの Base からクラスを継承します。そのため それらを特定のデータベース接続に関連付けることができます。

モデルクラスの簡単な例のため、 MyModel という名前のモデルを定義して います。

1
2
3
4
5
6
7
8
9
class MyModel(Base):
    __tablename__ = 'models'
    id = Column(Integer, primary_key=True)
    name = Column(Text, unique=True)
    value = Column(Integer)

    def __init__(self, name, value):
        self.name = name
        self.value = value

サンプルモデルの __init__ メソッドは 2 つの引数を取ります (namevalue)。 これらの値は __init__ 関数自身の中で self.name および self.value として保存されます。 MyModel クラスはまた、 __tablename__ 属性を持っています。これは、このクラスのインスタンス を表すデータを格納するために使用するテーブルを SQLAlchemy に通知します。

アプリケーションに含まれるモデル、ビュー、および初期化コードに関して、 これでおよそすべてです。