Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Pylons/Pyramid からの Django 逆入門

Nozomu Kaneko
September 13, 2013

Pylons/Pyramid からの Django 逆入門

Nozomu Kaneko

September 13, 2013
Tweet

More Decks by Nozomu Kaneko

Other Decks in Programming

Transcript

  1. おことわり • この発表は、 Pylons/Pyramid ユーザである 発表者がひょんなことから Django を使うことに なってからこれまでに経験したことを元に構成さ れています

    • Django 歴約半年(※2013年9月現在)のため、 間違いや不正確な説明があるかもしれません • マサカリは用法用量を守って正しくお使い下さい
  2. アジェンダ • 歴史 • 依存パッケージの数 • 機能面1: 似てるけどちょっと違う ◦ request

    オブジェクト / O/R マッパー ◦ テンプレートエンジン / ビュー ◦ コマンド / インタラクティブデバッグツール • 機能面2: 目的が同じでやり方が違う ◦ ルーティング / スキーママイグレーション ◦ フォーム / ユニットテスト / テストランナー ◦ 設定と拡張方法
  3. お前、誰よ 去年の発表@併設イベント • Pylons ユーザのための Pyramid 移行ガイド 普段の活動 • pylonsproject.jp

    の中の人 • Pyramid/Pylons はっかそん • Pyramid ドキュメント翻訳 • Python ドキュメント翻訳 • Sphinx にも少しだけコントリビュート
  4. Django の歴史 • 最初のリリースは2005年 • 最近は毎年1回程度メジャーリリースがある ◦ 2005年 最初のリリース ◦

    2007年3月 0.96 リリース ◦ 2008年9月 1.0 リリース ◦ 2009年7月 1.1 リリース ◦ 2010年5月 1.2 リリース ◦ 2011年3月 1.3 リリース ◦ 2012年3月 1.4 リリース ◦ 2013年2月 1.5 リリース
  5. Pyramid の歴史 • Pyramid 以前 ◦ Pylons 2006年〜2010年頃 ◦ repoze.bfg

    2008年〜2010年頃 ◦ 2010年頃 2つのプロジェクトが合流 ▪ プロジェクト名は Pylons を継承 ▪ Pyramid のコードベースは repoze.bfg がベース • Pyramid 時代 ◦ 最初のリリースは 2010年11月で、以降順調にバージョ ンアップを重ねている (最新の安定版は1.4) • Pyramid 以前を含めると歴史は Django と同じ くらい長いが、コードベースとしては5年くらいし か経っていない ウェブフレームワークの 名前 プロジェクト名
  6. $ pip install pyramid でインストールされるパッケージ一覧 依存パッケージの数 • Chameleon • Mako

    • MarkupSafe • PasteDeploy • WebOb • pyramid • repoze.lru • translationstring • venusian • zope.deprecation • zope.interface (RDB を使うなら必要) • SQLAlchemy • pyramid-tm • transaction • zope.sqlalchemy (開発時に必要) • waitress • pyramid-debugtoolbar • Pygments • WebTest • beautifulsoup4 • coverage • nose • six • zope.component • zope.event Pyramid は必要な機能は個別に インストールするようになっている
  7. request オブジェクト request.GET, request.POST の中身 Pyramid: webob.multidict.MultiDict Django: django.http.request.QueryDict Pyramid

    (WebOb) Django 単一の値を取得 (複数あればどれかの値) req.GET[key] req.GET.get(key) req.GET[key] req.GET.get(key) すべての値を取得 req.GET.getall(key) req.GET.getlist(key) 単一の値を取得 (複数なら例外) req.GET.getone(key) なし 単一または複数の値からなる 辞書を取得 req.GET.mixed() なし GET か POST の値を取得 req.params[key] req.REQUEST[key]
  8. request オブジェクト その他の値の取得方法 WebOb のドキュメントに詳細な比較があったorz http://docs.webob.org/en/latest/differences.html#django Pyramid (WebOb) Django HTTP

    ヘッダー req.headers[key] req.META[key] 文字コード req.charset req.encoding ファイルアップロード req.POST[key] req.FILES[key]
  9. 余談 Django の QueryDict には urlencode という便利メソッドがあ る。 >>> q

    = QueryDict( '', mutable=True) >>> q['next'] = '/a&b/' >>> q.urlencode(safe= '/') 'next=/a%26b/' WebOb だとこんな感じに書かないといけない。 >>> q = MultiDict() >>> q['next'] = '/a&b/' >>> urllib.urlencode([(k.encode( 'utf-8'), v.encode('utf-8')) for k, v in q.items()]) 'next=%2Fa%26b%2F'
  10. O/R マッパー Django の O/R マッパー: django.db.models from django.db import

    models class Group(models.Model): name = models.CharField(max_length=128) class User(models.Model): name = models.CharField(max_length=128) group = models.ForeignKey(Group) user = User.objects.filter(name='admin').get()
  11. Pyramid の O/R マッパー(ちょっと嘘) : sqlalchemy.orm from sqlalchemy import Column,

    ForeignKey from sqlalchemy.orm import relationship, backref from sqlalchemy.ext.declarative import declarative_base import sqlalchemy as sa Base = declarative_base() class Group(Base): __tablename__ = 'group' id = Column(sa.Integer, primary_key=True) name = Column(sa.String(128)) O/R マッパー
  12. O/R マッパー (続き) class User(Base): __tablename__ = 'user' id =

    Column(sa.Integer, primary_key=True) name = Column(sa.String(128)) group_id = Column(sa.Integer, ForeignKey('group.id')) group = relationship('Group', backref=backref('users', order_by=id)) user = DBSession.query(User).filter_by(name='admin').one()
  13. O/R マッパー 相違点3 • Django の .get() にあたるメソッドは SQLALchemy だと

    .one() ◦ SQLAlchemy の .first() はレコードがなければ None を 返す ◦ SQLAlchemy の .get() はプライマリキーを指定してロー ド済みのレコードを取得する
  14. O/R マッパー SQLAlchemy のいいところ order_summary = \ select([func.sum(Order.__table__.c.amount).label('amount'), Order.__table__.c.customer_id]) \

    .group_by(Order.__table__.c.customer_id).alias() class CustomerOrder(Base): __table__ = Customer.__table__.join(order_summary) 複数テーブルを結合した結果をマッピング http://pelican.aodag.jp/sqlalchemy.html
  15. テンプレートエンジン Django: 独自テンプレート言語 Pyramid: Mako, Chameleon, Jinja2 など自由に 選べる Django

    テンプレートは Jinja2 の多少不自由な版 だと思っていればだいたい困らない
  16. テンプレートエンジン • Jinja2 では普通に if が使える。 {% if name ==

    "Joe" -%} {%- endif %} (この例で % の内側の - は空白文字を除去する機能。 Django テンプレートにはもちろんそんなものはない)
  17. ビュー • Pylons: クラスベースのコントローラがビューを 担当。 • Pyramid: ビュー関数が基本。オプションでクラ スベースビューも使える。 •

    Django: 以前はビュー関数だけだったが、 1.3 からクラスベースビューがサポートされるように なった。
  18. Pyramid のクラスビュー: class BlogView(object): def __init__(self, request): self.request = request

    @view_config(route_name='blog_index', renderer='blog_index.jinja2') def index(self): ... @view_config(route_name='blog_article', renderer='blog_article.jinja2') def article(self): id = self.request.matchdict['id'] ... ビュー
  19. ビュー Django のクラスベースビュー class BlogIndexView(View): http_method_names = ['get', 'post'] def

    get(self, request, *args, **kwargs): ... def post(self, request, *args, **kwargs): ...
  20. 余談:Pylons 作者の後悔 Pylons の主要開発者である Ben Bangert は、フ レームワークがサブクラス化を要求するのは良くな い設計であるとしていくつかの理由を挙げている •

    ベースクラスのすべてのメソッドが API となり、 ベースクラスの実装は事実上凍結される ◦ 設計上あるいは実装上の欠陥を修正する際に 下位互換性を保つことが非常に困難な場合がある • サブクラス化による速度面のオーバーヘッド • 単体テストしづらい • 多重のオーバーライドによって生じる奇妙な問題
  21. コマンド シェルの起動 Django: $ python manage.py shell Pyramid: $ pshell

    development.ini 他のコマンドもだいたい似ている。
  22. インタラクティブデバッグツール Django: django-debug-toolbar Pyramid: pyramid_debugtoolbar Pylons: WebError (以前は paste EvalException)

    • エラー時にトレースバックを表示 • エラー時の変数の値を表示 • 実行されたSQL文を確認 • etc.
  23. ルーティング Pylons: def make_map(): map = Mapper(directory=config['pylons.paths']['controllers'], always_scan=config['debug']) map.connect( '/blog',

    controller='blog', action='index' ) map.connect( '/blog/{id}', controller='blog', action='article' ) return map URLパターン コントローラ、アクション(メソッド)
  24. ルーティング Pyramid: config.add_route('blog.index', '/blog/') config.add_route('blog.article', '/blog/{id}') • ビューの登録はルーティングの設定とは別に config. add_view

    か @view_config で行う • Pyramid には「トラバーサル」という別のルーティング方式も 用意されている URLパターン ルート名 (not ビュー)
  25. スキーママイグレーション south 現在の状態を表示 $ python manage.py migrate --list マイグレーションを実行 $

    python manage.py migrate マイグレーションスクリプトの自動生成 $ python manage.py schemamigration APP_NAME --auto
  26. スキーママイグレーション alembic 初期化 (最初の一回だけ) $ alembic init <dir> 現在の状態を表示 $

    alembic current $ alembic history マイグレーションを実行 $ alembic upgrade head マイグレーションスクリプトの自動生成 $ alembic revision --autogenerate
  27. スキーママイグレーション Django 1.7 の migration • 基本的な機能は south と同等 ◦

    ただしスクリプトの互換性はない • マイグレーションスクリプトが依存関係を持って いるらしい ◦ 同じ番号を持つマイグレーションスクリプトは、 自動的に直列化される
  28. フォーム • Django: django.forms (aka. newforms) • Pylons: 特に標準はないが、個人的には ToscaWidgets

    + FormEncode が好きだった • Pyramid: deform + colandar がデファクト スタンダード
  29. フォーム Django を使ったバリデーション: form = MyForm(request.POST) if form.is_valid(): data =

    form.cleaned_data # do something ... return HttpResponseRedirect('/thanks/') return render_to_response('contact.html', {'form': form})
  30. フォーム ToscaWidgets を使ったバリデーション: form = MyForm() try: value = form.validate(request.POST.mixed())

    except Invalid: c.form = form return render('/contact.html') # do something ... return redirect(h.url_for(action='thanks'))
  31. フォーム deform を使ったバリデーション: form = Form(MySchema(), buttons=('submit',)) try: appstruct =

    form.validate(request.POST.items()) except ValidationFailure, e: return {'form': e.render()} # do something ... return {'form': None, 'appstruct': appstruct}
  32. フォーム Django form の許せないところ: • is_valid() が副作用を持つ ◦ deform や

    ToscaWidgets ではバリデーションされた値 が返り値として得られる • フィールド名 (CharField) ◦ モデルにフィールド定義が結びついていた過去 (Django 1.0 以前のフォーム)を引きずっている?
  33. ユニットテスト Django: django.test.TestCase Pyramid: unittest.TestCase (Python 標準) • Django の

    TestCase は unittest のTestCase を継承して、さらに独自の拡張をしている ◦ テスト毎の環境準備 ▪ DB トランザクションの開始とクリーンアップ ▪ urlconf の設定 ▪ fixture のロード ◦ assert 系メソッドの追加
  34. テストランナー Django: DjangoTestSuiteRunner Pyramid: nose, py.test • Django のテストランナーは、アプリケーション ディレクトリ内の

    tests モジュールをインポートし ようとする ◦ models.py がないと「アプリケーションが見つからない」 というエラーになる (ハマりどころ) ◦ tests/__init__.py でインポートを忘れるとテストが実行さ れない (ハマりどころその2)
  35. テストランナー Django にはない nose の便利なところ • モジュール名、クラス名、メソッド名に test とついているもの を自動的に見つけてくれる

    • 引数で特定のテストケースだけを指定して実行することがで きる(テストの開発時に便利) Djangoでもできた • モジュール単位の setup/teardown
  36. テストランナー Django にはない nose の便利なところ(続き) • --pdb オプション: エラーが起きたときにデバッガが自動的に 起動する

    ◦ django-pdb をインストールすると Django でも使えるよ うになる • --with-coverage オプション: coverage モジュールを使って テストのカバレージを計測する ◦ django-coverage をインストールすると Django でも同 じことができる
  37. 設定と拡張方法 機能拡張 • Django: Django ミドルウェアを使う • Pyramid: tween やイベントを使う

    • Pylons: WSGI ミドルウェアを使う 設定ファイル • Django: settings.py に設定を書く • Pyramid, Pylons: development.ini に設定を 書く
  38. まとめにかえて • Django をほとんど知らずにプロジェクトに 参加して、 1 週間くらいでなんとなく使えるよう になった ◦ Pyramid/Pylons/WSGI

    の知識が生きた ◦ 何か分からないことがあったときに、基本的に Django だけを見れば良いので学習が楽
  39. まとめにかえて • Django はフルスタックなフレームワーク。 それは長所でもあり短所でもある ◦ Django しか使ったことがない人は他にもっと良い ライブラリがあることを知ってほしい ◦

    他のフレームワークのユーザは、 Django の高度に 統合されたモジュール群の便利さを見習うべき ▪ 特に、個々のパーツを組み合わせて使うときの ドキュメントが充実している