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

Pylons/Pyramid からの Django 逆入門

33a639236e5fa989dde2f579202ec18a?s=47 Nozomu Kaneko
September 13, 2013

Pylons/Pyramid からの Django 逆入門

33a639236e5fa989dde2f579202ec18a?s=128

Nozomu Kaneko

September 13, 2013
Tweet

Transcript

  1. Pylons/Pyramid からの Django 逆入門 Nozomu Kaneko PyCon APAC 2013 in

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

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

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

    の中の人 • Pyramid/Pylons はっかそん • Pyramid ドキュメント翻訳 • Python ドキュメント翻訳 • Sphinx にも少しだけコントリビュート
  5. 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 リリース
  6. Pyramid の歴史 • Pyramid 以前 ◦ Pylons 2006年〜2010年頃 ◦ repoze.bfg

    2008年〜2010年頃 ◦ 2010年頃 2つのプロジェクトが合流 ▪ プロジェクト名は Pylons を継承 ▪ Pyramid のコードベースは repoze.bfg がベース • Pyramid 時代 ◦ 最初のリリースは 2010年11月で、以降順調にバージョ ンアップを重ねている (最新の安定版は1.4) • Pyramid 以前を含めると歴史は Django と同じ くらい長いが、コードベースとしては5年くらいし か経っていない ウェブフレームワークの 名前 プロジェクト名
  7. © aodag プロジェクト関係図

  8. 依存パッケージの数 $ pip install django でインストールされるパッケージ一覧

  9. 依存パッケージの数 $ pip install django でインストールされるパッケージ一覧 • Django Django は必要な機能はすべて自前で

    持っている
  10. $ pip install pyramid でインストールされるパッケージ一覧 依存パッケージの数

  11. $ 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 は必要な機能は個別に インストールするようになっている
  12. request オブジェクト Pyramid: webob.request.BaseRequest Django: django.http.HttpRequest HTTP GET/POST パラメータの取り方は同じ if

    request.method == 'POST': spam = request.POST['spam']
  13. 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]
  14. 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]
  15. 余談 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'
  16. 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()
  17. 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 マッパー
  18. 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()
  19. O/R マッパー 相違点1 • Django はプライマリキーや foreign キーに対 応するメンバーを自動的に作るが、 SQLAlchemy

    はそんなことはしてくれない
  20. O/R マッパー 相違点2 • Django のクエリオブジェクトはモデルに 紐づいているが、SQLAlchemy はモデル だけでなくカラムから作ることもできる >>>

    query = DBSession.query([User.name, User.fullname]) >>> for name, fullname in query: ... print name, fullname
  21. O/R マッパー 相違点3 • Django の .get() にあたるメソッドは SQLALchemy だと

    .one() ◦ SQLAlchemy の .first() はレコードがなければ None を 返す ◦ SQLAlchemy の .get() はプライマリキーを指定してロー ド済みのレコードを取得する
  22. O/R マッパー 相違点4 • オブジェクトがなかったときの例外が Django で はモデルごとに異なる try: user

    = User.objects.get(name=”user”) except User.DoesNotExist: pass
  23. O/R マッパー SQLAlchemy のいいところ DBSession.query(User).filter( User.name.in_(['jack', 'wendy'])).all() in がキモくない

  24. O/R マッパー SQLAlchemy のいいところ DBSession.query(Address).join(Address.user) \ .filter(or_(User.name.like('%ed'), func.length(User.name) > 3)

    どんな複雑な SQL 文でも書ける
  25. 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
  26. テンプレートエンジン Django: 独自テンプレート言語 Pyramid: Mako, Chameleon, Jinja2 など自由に 選べる Django

    テンプレートは Jinja2 の多少不自由な版 だと思っていればだいたい困らない
  27. テンプレートエンジン フィルタ • フィルタは Django でも使える ◦ ただし | の前後にスペースを入れるとテンプレートエラー

    になるのはダサいと思う {{ name|upper }}
  28. テンプレートエンジン 条件分岐 • Django テンプレートでは任意の式は書けない。 そのため比較は専用のタグを使う。 間違い。最近の Django では式が書けます {%

    ifequal name "Joe" %} {% endifequal %}
  29. テンプレートエンジン • Jinja2 では普通に if が使える。 {% if name ==

    "Joe" -%} {%- endif %} (この例で % の内側の - は空白文字を除去する機能。 Django テンプレートにはもちろんそんなものはない)
  30. テンプレートエンジン テンプレートタグ • Django にはテンプレートからユーザ定義の関 数を呼ぶ機能がある。 {% url app_views.client client.id

    %} Jinja2 ならグローバルオブジェクトか extensions を使う。
  31. ビュー • Pylons: クラスベースのコントローラがビューを 担当。 • Pyramid: ビュー関数が基本。オプションでクラ スベースビューも使える。 •

    Django: 以前はビュー関数だけだったが、 1.3 からクラスベースビューがサポートされるように なった。
  32. ビュー Pylons のコントローラ: class BlogController(BaseController): def index(self): ... return render('blog_index.jinja2')

    def article(self, id): ... return render('blog_article.jinja2')
  33. 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'] ... ビュー
  34. ビュー Django のクラスベースビュー class BlogIndexView(View): http_method_names = ['get', 'post'] def

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

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

    --reload development.ini
  37. コマンド シェルの起動 Django: $ python manage.py shell Pyramid: $ pshell

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

    • エラー時にトレースバックを表示 • エラー時の変数の値を表示 • 実行されたSQL文を確認 • etc.
  39. WebError 変数の値を見るだけでなく、任意の式を評 価することができる

  40. pyramid_debugtoolbar

  41. Django エラー画面

  42. django-debug-toolbar

  43. ルーティング Django: urlpatterns = patterns('', url(r'^blog/$', 'views.blog_index_view'), url(r'^blog/(\d+)$', 'views.blog_article_view'), )

    URLパターン ビュー
  44. ルーティング 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パターン コントローラ、アクション(メソッド)
  45. ルーティング Pyramid: config.add_route('blog.index', '/blog/') config.add_route('blog.article', '/blog/{id}') • ビューの登録はルーティングの設定とは別に config. add_view

    か @view_config で行う • Pyramid には「トラバーサル」という別のルーティング方式も 用意されている URLパターン ルート名 (not ビュー)
  46. スキーママイグレーション • Django の場合は south を使う • Django 1.7 からは標準機能に含まれる

    • SQLAlchemy なら alembic が使える
  47. スキーママイグレーション south 現在の状態を表示 $ python manage.py migrate --list マイグレーションを実行 $

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

    alembic current $ alembic history マイグレーションを実行 $ alembic upgrade head マイグレーションスクリプトの自動生成 $ alembic revision --autogenerate
  49. スキーママイグレーション alembic のすごいところ • ブランチが作れる • マイグレーションを実際には行わずに、 SQL 文 だけを出力することができる

    (オフラインモード)
  50. スキーママイグレーション Django 1.7 の migration • 基本的な機能は south と同等 ◦

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

    + FormEncode が好きだった • Pyramid: deform + colandar がデファクト スタンダード
  52. フォーム 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})
  53. フォーム 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'))
  54. フォーム 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}
  55. フォーム Django form の許せないところ: • フォームとスキーマが独立していない ◦ スキーマ単体で使いたい ◦ 複数のフィールドの値に関連するバリデーション

    ロジックをフォームクラスに書かないといけない
  56. フォーム Django form の許せないところ: • is_valid() が副作用を持つ ◦ deform や

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

    TestCase は unittest のTestCase を継承して、さらに独自の拡張をしている ◦ テスト毎の環境準備 ▪ DB トランザクションの開始とクリーンアップ ▪ urlconf の設定 ▪ fixture のロード ◦ assert 系メソッドの追加
  58. ユニットテスト Django の TestCase でハマったこと: • テストを追加したら、単体では動くのに 他のテストと一緒に動かすとコケる ◦ 複数のデータベース設定を持つアプリケーションで、テ

    ストケースに multi_db = True を書いていなかったこと が原因
  59. テストランナー Django: DjangoTestSuiteRunner Pyramid: nose, py.test • Django のテストランナーは、アプリケーション ディレクトリ内の

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

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

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

    • Pylons: WSGI ミドルウェアを使う 設定ファイル • Django: settings.py に設定を書く • Pyramid, Pylons: development.ini に設定を 書く
  63. 設定と拡張方法 • tween と Django ミドルウェアはほぼ同じ ◦ WSGI ミドルウェアにはフレームワーク非依存で 共通して使えるという利点があったが、

    tween も Django もそれぞれのフレームワーク専用という 点がやや残念
  64. 設定と拡張方法 • settings.py は Python ファイルなので自由度は 高い • 反面、複雑になると問題が起きたときのデバッ グが大変

    ◦ 開発用/ステージング用/本番用で設定を一部共有したい ときとか
  65. 設定と拡張方法 • ロギングやデータベースの設定方法など、 Django 独自の書き方を覚える必要がある ◦ Pyramid の場合は logging.config や

    sqlalchemy dburi の書式がそのまま使える
  66. まとめにかえて • Django をほとんど知らずにプロジェクトに 参加して、 1 週間くらいでなんとなく使えるよう になった ◦ Pyramid/Pylons/WSGI

    の知識が生きた ◦ 何か分からないことがあったときに、基本的に Django だけを見れば良いので学習が楽
  67. まとめにかえて • Django が不便だと思うこともあるが、 探せばたいてい解決策やサードパーティの ライブラリが見つかる ◦ コミュニティの力は偉大

  68. まとめにかえて • Django はフルスタックなフレームワーク。 それは長所でもあり短所でもある ◦ Django しか使ったことがない人は他にもっと良い ライブラリがあることを知ってほしい ◦

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