読者です 読者をやめる 読者になる 読者になる

例えば、Djangoを避ける

Django

8/25にサイボウズ・ラボにて行われた「Django勉強会 Disc 4」での発表資料を公開します。
途中のMigrateの説明は少々簡略化しすぎで、これでは初めての方は実際に使えないかと思いますが、別途現在のMigrateの状況とともに紹介しようと思っています。

【追記】
Migrateについては別の場所にて紹介していますので、興味のある方は参照してみてください。
http://labs.unoh.net/2007/09/migrate.html

自己紹介

ウノウでgumiという携帯サイト作っています。

  • 最近は本当に携帯サイトにしか興味がない。
  • JavaScript?何それ?
  • CSS?何それ?

ちなみにgumiの環境

なぜ「Djangoを避ける」のか?

  • 別に避けません><バリバリ使っています。
  • でも、「Djangoでどうやるの?」というのに悩むぐらいならば、Djangoの流儀にこだわらない方が早い。

例えば、

携帯対応 - 課題

  • 端末情報を取得
  • 絵文字
  • 端末の画面サイズ
  • 各端末のブラウザの表示の違い

携帯対応 - 対処法

from uamobile import detect
try:
    from threading import local
except ImportError:
    from django.utils._threading_local import local

_thread_locals = local()
def get_current_device():
    return getattr(_thread_locals, 'device', None)

def get_current_request():
    return getattr(_thread_locals, 'request', None)

class UserAgentMobile(object):
    def process_request(self, request):
        _thread_locals.request = request

        dev = detect(request.META)
        request.device = dev
        _thread_locals.device = dev

c.f.
http://labs.unoh.net/2007/08/python_3.html

ThreadLocalを使うメリット

  • 現在のリクエストや端末情報を任意の場所から参照できる。
  • 現在のセッションを任意の場所から参照できる。

ThreadLocalを使うデメリット

  • Django的なやり方ではない。
  • 本当はアプリケーションやフレームワークのデザインで解決すべき問題。

文字コード/絵文字

ベストプラクティスとしては・・・

DoCoMo
Shift_JIS
au
Shift_JIS
SoftBank
UTF-8
WILLCOM
UTF-8 or Shift_JIS

文字コード/絵文字 - 現実

  • SoftBankの一部の端末の絵文字の問題を無視するなら、Shift_JISで十分。
  • Pythonの内部エンコーディングをcp932にする(超重要)。

文字コード/絵文字 - 悩み

  • Unicode-branchは動かないかも。
  • 端末によってShift_JISな応答を返したり、UTF-8な応答を返すには?
  • DoCoMoならばX、それ以外ならばYのような分岐をしていると、テンプレートが読めなくなる。

絵文字 - モデル・データベース

  • 各キャリアの絵文字をいったん共通コードに変換する。
  • 出力時に共通コードを表示端末ごとにエンコードして出力する。

絵文字 - フォーム

  • newformsを使用している。
  • newformsをラップして、モバイル対応のMobileForm, MobileInput, MobileTextarea, EmojiFieldを定義する。
  • このformから取得した値は、共通コードに変換済みの値となる。

絵文字 - テンプレート

フィルターを使う

  {{ message|escape|emoji }}<br />
  {{ message.user.nickname|escape|emoji }}

絵文字 - テンプレート

ブロックを使う

  {% emojiblock %}
    {% trans "Your mail was sent successfully." %}
  {% endemojiblock %}

絵文字 - テンプレート

タグを使う

  {% emoji "smile" %}{% trans "Hello, World" %}<br />

絵文字の相互変換

  • ウノウに代々受け継がれてきた変換テーブルを使っている。
  • 余裕ができたら、他のライブラリと合わせて、オープンソースで公開したい。

c.f.
http://labs.unoh.net/2007/02/post_65.html

Djangoで携帯サイト開発

  • DjangoのMTVデザインはクリーン&エレガント。
  • だけど「リクエストスコープ」という概念は欠けているように思う。
  • 携帯サイト開発にはDjangoはベストな選択ではないかも。

ORM

Not Django, SQLAlchemy

SQLAlchemyを選択した理由

  • 「Djangoでどうやるか」ではなく、「データベース設計としてどうか」から考え始めることができる。
  • クエリ、トランザクションを制御しやすい。
  • 複数データベース対応。

基本的な処理のパターン

@login_required
def update_profile(req, field_id):
   detail = ProfileDetail.get_by(user_id=req.user.id,
                                 field_id=field_id)
   if not detail:
       raise Http404
  
   if req.method != 'POST':
       form = ProfileEditForm(initial={'value':detail.value})
   else:
       form = ProfileEditForm(req.POST)
       if form.is_valid():
           detail.value = form.clean()['value']
           detail.flush()
           return HttpResponseRedirect('/my/profile/')
  
   return render_to_response('profiles/profile_edit_form.html',
                             {'form'  : form,
                              'detial': detail},
                             context_instance=RequestContext(req))

SQLAlchemyを使用してみて

  • ORMとしての完成度が高い。
  • 更新・挿入はORMを使い、読込は直接SQLを書くようなことをやってるよ。
  • MapperExtensionを使って更新・挿入・削除にフックをかけられる。

今後のSQLAlchemyにも期待

  • DBの垂直分割
  • DBの水平分割

マイグレーション

  • SQLAlchemyベースのMigrateを使う。
  • これは本当に使える。

c.f.
http://erosson.com/migrate/docs/index.html

migrateの使用法

$ python migrate/manate.py script script.py

script.pyを書く

from sqlalchemy import *
from migrate import *
   
def upgrade():
    table = Table('cat',
                  Column('id', Integer, primary_key=True),
                  Column('name', Unicode(50), nullable=False),
                  mysql_engine='InnoDB'
                  )
    table.create(migrate_engine)
   
def downgrade():
    migrate_engine.execute("DROP TABLE cat")

テストする

$ python migrate/manate.py test script.py
Upgrading... done
Downgrading... done
Success

コミットする

$ python migrate/manate.py commit script.py

$PROJECT/migrate/versions//.pyとしてコミットされる。

アップグレードする

$ python migrate/manate.py upgrade

ダウングレードする

$ python migrate/manate.py downgrade \
  "mysql://scott:tiger@localhost/emp?charset=utf8" \
  <repository_name> <version_number>

migrateの問題点

開発がとまっちゃった><

migrateの代替は?

c.f.
http://jjinux.blogspot.com/2007/08/python-database-migrations_07.html

フロントとバックヤード

  • フロント(ユーザが使うサービス)の開発優先。
  • でもバックヤード(管理系)の開発タスクも発生してくるよね。
  • そんな時、django.contrib.adminはやっぱり便利。

モジュールを分ける

  • フロントはgumiモジュール。普通じゃないDjangoアプリケーション。
  • バックヤードはgumiadminモジュール。普通のDjangoアプリケーション。

フロントはSQLAlchemyで

from sqlalchemy import *
message = Table('message', metadata,
                Column('id', Integer, primary_key=True),
                Column('user_id',
                       Integer,
                       ForeignKey(users.c.id, ondelete='CASCADE'),
                       nullable=False),
                Column('content', Unicode, nullable=False),
                Column('posted_at', DateTime, default=func.now(), nullable=False),
                mysql_engine='InnoDB'
                )

バックヤードはdjango.dbで

from django.db import models
   
class Message(models.Model):
    content   = models.TextField()
    posted_at = models.DateTimeField()
       
    class Meta:
        db_table = 'message'
       
    class Admin:
        pass

django.contrib.adminを利用する

  • これは結構上手くいく!
  • 正しいデータベース設計が重要。
  • フロントをPHPRails、バックヤードをDjangoという選択肢もありだと思っている。

テスト

  • noseを使ってるよ。
  • 携帯各キャリア+NonMobileでのビューのテストをきれいに書く方法を模索中。
  • テストは書くことが重要。無理にDjangoの流儀に従う必要はないと思う。

デプロイ

  • capistranoを使用しているよ。
  • 無理にPythonで代替を探したり、自作したりする必要はないと思う。

ジョブキュー

  • メール送信。
  • 動画変換処理のキューイング。
  • etc.
  • たぶんPerl製のTheSchwartz and/or Gearmanを使う。

QRコードの生成

  • Pythonでは良いライブラリがない。
  • PHP, Perl, Rubyのライブラリへのスイッチを検討中。
  • 非同期に生成しているのでPythonであることにはこだわっていないよ。

まとめ

  • 適度にDjangoを利用し、適度にDjangoを避けるのがベスト。
  • 適度にPythonを利用し、適度にPythonを避けるのがベスト。

おまけ - なぜPylonsを使わないのか?

  • バージョンアップの影響が大きすぎる。
  • RouteよりもDjangoの正規表現によるURLマッピングの方がアプリケーション全体を俯瞰しやすい?
  • モジュール構成がDjangoほどエレガントではない。

Djangoの優位性

  • モジュール間の疎結合を実現しやすい。
  • 使わなくなったらsettings.pyのINSTALLED_APPSから外せばよい。
  • Pylonsは薦めないけど、使いたい人がいるなら頑張って!

おしまい

ご清聴ありがとうございました。