伝説のギタリストじゃない方のDjango

  • 1,560 views
Uploaded on

2006年1月にPythonWorkshop03でDjangoを紹介した時のスライドです。

2006年1月にPythonWorkshop03でDjangoを紹介した時のスライドです。

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
1,560
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
8
Comments
0
Likes
1

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • Pythonistaを目指しています。
    Pythonは一番敷居の高い言語なので、到達は困難だとおもっています。
    何で困難かというと、職場ではPythonのコードが書けるほかにも「デザインがすらっと美しくできる」「根性系でないアウトドアっぽいスポーツが出来る」ことをPythonistaの要件としているからです。
    で、Djangoです。PythonのWebフレームワークです。ライセンスはBSD。
  • Djangoをググルと、上位はDjango Reinhardtに関するものばかりでてきます。流石に本家はページランクが上がってかなり上位に出てくるようになりました、私のブログはあまり上にきません。
    よくDjangoはafter Railsとしてリストされますが、開発自体はRailsと同時期に開始されています。
    Djangoは地方新聞のサイト構築のために作り上げられてきたフレームワークなので、CMS的なものに対する側面は非常に強いです。ワシントンポストの数百万件のデータを保持している議決データベースサイトにも利用されています。ワシントンポストには最近新しいDjangoでできたアプリケーションが増えたそうなので、ワシントンポストには気に入られたようです。
  • Webアプリケーションフレームワークには無いとはいえなくなってきたO/Rマッピングとか、Djangoのサイトでメイン機能とされている物たちをリストしてみました。
    新聞系サイトなのでキャッシュは特に力が入っています。ここのところ業務システムをやっている自分としては、阿呆としか思えないキャッシュがあります。
    i18nはオープンソース化してすぐに取り入れられているので、Admin Interfaceなどが既に数十カ国語に対応しています。
    日本語も怪しい訳のものが最近の版には入っています。
  • DRYの原則というのは、ドメインモデルに関する情報は全て一カ所に存在するべきとするものです。データベースのER図やらXMLの設定ファイルやら、ソースコードやらに設定が分散すべきでないということです。 Railsのデータベースを基本とするアプローチとは反対に、Djangoはmodelを基本としたアプローチを行います。 ただしModelベースのDRYはあくまで原則なので、レガシーなデータベースと連携する場合用に、リバース(inspectdb)も’一応’用意されています。
    ユニークキーやIndexの設定までもModelに記述できます。
    現時点では黒魔術的な部分がありますが、バージョン0.92では多くの黒魔術が取り除かれますので、コードから追えないものが嫌いな人も大丈夫です。
  • なかなか美しい。
    操作フローの美しさはTurboGearsのAJAXを使用したパターンに敵いませんが見た目はきっと一番美しいです。
    modelで記述した制限は自動でバリデータが適用されますので、modelで表現できる制限のみのデータ管理画面は自動で出来る物で十分でしょう。関連も、ある程度の検索も容易に設定できます。
    あとで時間があれば画面をお見せします。
  • Zopeとかを利用されている方の場合は、見慣れたURLだと思います。
    正規表現で記述をし、マッチした引数をコントローラにあたるファンクションに渡します。
    urls.pyにマッチしない場合はHTTPの404エラーが発生します。
    後で説明するキャッシュで重要になってくるので、Elegantにしておきましょう。
  • PloneのMETALと違って、HTMLが壊れるタイプのテンプレートです。
    Djangoプロジェクトの人としては、「デザイナーも馬鹿にせずにちょっと教えれば、コードまで書き出すはず」という思想で、事実彼らの周りのデザイナーはテンプレートを使いこなしているようです。
    個人的には「テンプレートの継承」というのは好きではないんですが、似ていて少し違うと言ったテンプレートは継承した方がいい場面もあるでしょう。
  • サイト全体をキャッシュする設定にするのは、settings.pyに3行追加するだけです。
    コントローラにすこしコードを追加すれば細かく設定することも出来ます。
    URLの設計が重要なのは、GET/POSTのデータがある場合にはキャッシュを利用しないため。ElegantURLにしないとほとんどキャッシュが効かないでしょう。会員50万人、常時2万セッションという規模のポーランド?のSNSではtomcatサーバ4台からDjangoサーバ1台にリプレースしたということです。
  • キャッシュはlocmemの他にも、memcachやdb,fileがあります。
    memcashは本家スラドも使用している模様です。スタティックファイルの書き出しとかmemcashを使うような独自実装とかが必要なく、ビュー毎にキャッシュするかどうかの制御をする場合も各ビューに1・2行記述するだけなので、開発は非常に楽だとおもいます。実際はいろいろとURL等の設計が必要でしょうが。
  • pythonの人には見慣れているであろう、poを使います。
    国際化用のコードの記述は簡単です。
    po、moはこんな感じに作ります。getTextというライブラリが必要なので、ウィンドウズでは面倒かもしれない。コード書きや動作には必要ないので、まとめてxUnix系のOSでやった方が楽。OSXさいこー。
    ploneとかのようにaccept-languageで言語を切り替えたり、sessionやcookieの特定の値をものに言語を切り替えたり出来ます。
  • ここまでで、Djangoに興味がわいたはずなので、実際的な話にうつります。
    mod_pythonの3.xが必要なので、apacheのバージョンも2.xです。
    lighttpdとfast-cgiという手もあります。
    対応データベースの、SQLServerは現時点ではADO_MS_SQLServer限定です。よく意味はわかりませんが、限定です。
    Oracleに関しては以前パッチを見かけましたが、使えるかどうかは不明です。
  • 実際の開発は、プロジェクトの開始からしなければいけないことが決まっています。
    フレームワークですから。
  • 一つのデータベースに対して、一つのDjango-Coreデータベースが必要です。それを用意するためには、プロジェクトを開始してデータベースの設定をする必要があります。順番がちょっと気持ち悪いです。
    一度プロジェクトを開始してしまえば、同じデータベース情報を利用するプロジェクトや、そのプロジェクトは以下のアプリケーションは同一Django-Coreデータベースを利用できる。
  • とばす。変わっているのは、まずProjectのイニシャライズをするという所。
    データベースの情報が必要なので、initよりもstartprojectが先なのがちょっとイメージわきにくいところです。
    一つのプロジェクトで複数のアプリケーションを作る場合は、startappからを繰り返すことになります。
    manage.pyというスクリプトは、django-admin.pyとsettingsオプションのラッパーです。startproject以外でもdjango-admin.pyは利用可能です。
  • django-admin.pyというのは、Djangoの操作スクリプトです。Djangoに含まれます。
    この操作ではカレントディレクトリにプロジェクト用のディレクトリが作成されます。
    プロジェクトディレクトリの直下にsettings.pyというファイルが生成されていますが、これはプロジェクトの設定を記述するファイルです。
    manage.pyというのはdjango-admin.pyのラッパーです。プロジェクトの作成以降はラッパを使った方が便利です。
  • まずsettings.pyを編集します。
    現バージョンではプロジェクト毎に一つのデータベースしか使用できません。
    settings.pyには他にもいろいろな設定がありますが、はしょります。
    コメントを読めば大抵わかります。
  • 自動管理インターフェースで使用するテーブルや、アプリケーションで使用する情報も格納されます。
    データベース自体や接続ユーザは作成されませんので、使用するデータベースと接続ユーザは事前に作成しておく必要があります。
  • アプリケーションを開始するには、manage.pyを使用してstartprojectします。
    プロジェクトにアプリケーションが一つの場合は、なんだか間抜けになってしまいます。
    WorkStyleがプロジェクトの一部で或る場合なんかはしっくりくると思いますが。
  • DRYの原則があるので、結構細かいModelになります。
    class METAという部分にはいろいろ記述することが出来ます(dbの設定と関係のないバリデータとか)。
    TurboGearsとか見たことある人は似てると感じるでしょう。
    PKは自動的にidという変数名で生成されます。
    DRYは原則なので、テーブルやカラムの名前との対応は変更することも出来ます。
  • インストールを行うと、データベースにModelで定義したテーブルが作成されます。
    同時に、アプリケーション情報がDjangoコアテーブルに登録されます。
    Indexは自動では登録されないので、sqlindexesを引数に与えてsqlを出力させて、手動で実行。
    テーブル名はアプリケーション名とModel名(複数形)で作成される。
  • ここまでで、O/Rが使用可能になっていますので、Pythonスクリプトから使ってみます。
    DJANGO_SETTINGS_MODULEという環境変数をセットしておく必要があります。

  • urls.pyはアプリケーション毎に分けたいので、プロジェクト直下のurls.pyはインクルードを行っているだけです。
    取得と更新を同一URLにすると、更新時にエラーが出た際の処理で取得と同じ物を使い回せるので、同一にします。
    取得と更新のどちらで呼ばれたのかを判別するのはGETかPOSTかを利用します。

  • 名前はビューですが、実際はコントローラです。
    URLのところでtask_idとなっていた物が、edit_taskのtask_idに渡されます。第一引数のrequestというオブジェクトは名前から想像できるとおりの物です。render_to_responseというファンクションの結果を返していますが、これはdjangoのデフォルトテンプレートシステムを使用してレスポンスを返すショートカットファンクションです。とにかくレスポンスを返せばよいので、別のテンプレートシステムを使用するのも、XMLやPDFを返すのも自由です。
    ビューは、リクエストを受け取って処理をしてレスポンスを返す。それだけのものです。

  • DRYの原則があるので、入力値validateもModelの情報から行われます。
    自動マニピュレータは追加用と変更用の2種類があります。
    Modelに記述した定義とModelのMETAに記述したカスタムバリデータが呼び出されます。
    面白いのは値を詰め直さなくてもそのまま保存できることです。徹底して無駄を省いています。
  • 現実的にはModelと1対1のデータがサブミットされることは少ないので、カスタムManipulatorを作成します。
    自動をいくつか通すという手もあると思いますが、いまのところどうするのがよいのかわかりません。
    あと、この例で使用しているisValidTagNameはここではなくてModelのMETAに記述して実際は問題ありません。
    使用方法は簡単です。自動の物との違いは、カスタムManipulatorを使っての保存が出来ないことくらいでしょう。
  • 設置ディレクトリは複数設定可能です。
    今回は継承を使った例でいきます。
    まず作成したのは、テンプレートディレクトリの下のworkstyle/TaskForm.htmlです。アプリケーション名を入れているのがポイントです(ビューからはworkstyle/TaskFormと洋舞のですが、こうすれば重複しにくくなります)。
    テンプレートのデフォルトの拡張子は.htmlです(変更可能)。
    {%がif文などの文を表し、{{が変数の出力です。
    forループやループカウンター等、大抵の必要な物はそろっています。フィルターも結構な数あります。
    継承は、ベースにあるブロックを継承先の記述に置き換えます(継承先ではブロック以外は不要)。
    (余裕があったらコード)

  • モジュールでLibraryにレジストして、ビューでもインポートをし、テンプレートでもロードをします。
    ちょっと面倒ですが、便利な機能です。
  • 本来は先ですが・・・。
    greenpeaceのmeltというプロジェクトではtwilというライブラリを利用してテストをしている模様です。
    ちょっとまだいろいろ調査不足です。
  • 放置してあるチュートリアル等の訳は、バージョン1.0前後で手を入れようと考えています。
  • 以上、駆け足ですが、紹介自体は終わります。

Transcript

  • 1. Django
  • 2. Django biography Django Reinhardt 1910 Django 2003 2005 7 Rails discography www.ljworld.com www.lawrence.com projects.washingtonpost.com/contress/
  • 3. main features
  • 4. main features O/R Mapper Automatic Admin Interface Elegant URL Design Template Cache i18n
  • 5. O/R Mapper
  • 6. O/R Mapper DRY Model
  • 7. O/R Mapper DRY Model Model Database
  • 8. O/R Mapper DRY Model Model Database SQLObject SELECT keyword args ForeignKey model Index Model
  • 9. Automatic Admin I/F
  • 10. Automatic Admin I/F model rails scafford TurboGears catwalk
  • 11. Automatic Admin I/F model rails scafford TurboGears catwalk
  • 12. Automatic Admin I/F model rails scafford TurboGears catwalk
  • 13. Elegant URL Desgin
  • 14. Elegant URL Desgin URL emacs PROJECT/apps/project/urls.py (r'^Task/(?P<task_id>d+)/edit/$','Project.apps.project.views.edit'), emacs PROJECT/apps/project/views.py def edit(request, task_id) : do_somthing
  • 15. Elegant URL Desgin URL http://host/WorkStyle/Task/5/edit/ emacs PROJECT/apps/project/urls.py (r'^Task/(?P<task_id>d+)/edit/$','Project.apps.project.views.edit'), emacs PROJECT/apps/project/views.py def edit(request, task_id) : do_somthing
  • 16. Elegant URL Desgin URL http://host/WorkStyle/Task/5/edit/ 5 emacs PROJECT/apps/project/urls.py (r'^Task/(?P<task_id>d+)/edit/$','Project.apps.project.views.edit'), emacs PROJECT/apps/project/views.py def edit(request, task_id) : do_somthing
  • 17. Elegant URL Desgin URL emacs PROJECT/apps/project/urls.py (r'^Task/(?P<task_id>d+)/edit/$','Project.apps.project.views.edit'), emacs PROJECT/apps/project/views.py def edit(request, task_id) : do_somthing
  • 18. Template
  • 19. Template {{ XXX:escape }} {% if task.update_date %} <tr> <th nowrap="nowrap">{% trans "Last Update" %}</th> <td>{{ task.update_date|date:"Y/m/d" }}</td> </tr> {% endif %}
  • 20. Template {{ XXX:escape }} {% if task.update_date %} filter <tr> <th nowrap="nowrap">{% trans "Last Update" %}</th> <td>{{ task.update_date|date:"Y/m/d" }}</td> </tr> {% endif %}
  • 21. Template {{ XXX:escape }} Tag {% if task.update_date %} <tr> <th nowrap="nowrap">{% trans "Last Update" %}</th> <td>{{ task.update_date|date:"Y/m/d" }}</td> </tr> {% endif %}
  • 22. cache
  • 23. cache 3 HTTP
  • 24. Cache cache locmem( WorkStyle sqlite3 SQL 5 O/R :P4-2.4GHz+1GBMem, Gentoo, apache:2.0.54, mod_python3.x :iBookG4-800MHz, 640MBMem, ApacheBench1.3d 1000request 10concurrency
  • 25. Cache cache locmem( WorkStyle sqlite3 SQL 5 O/R :P4-2.4GHz+1GBMem, Gentoo, apache:2.0.54, mod_python3.x :iBookG4-800MHz, 640MBMem, ApacheBench1.3d 1000request 10concurrency normal apache locmem totaltime 114.98 20.95 5.05 rec/sec 8.7 47.72 197.82
  • 26. Cache cache locmem( WorkStyle sqlite3 SQL 5 O/R :P4-2.4GHz+1GBMem, Gentoo, apache:2.0.54, mod_python3.x :iBookG4-800MHz, 640MBMem, ApacheBench1.3d 1000request 10concurrency total time Request/sec 200 150 normal apache locmem 100 totaltime 114.98 20.95 5.05 50 rec/sec 8.7 47.72 197.82 0 normal apache locmem
  • 27. i18n
  • 28. i18n po settings.py Automatic Admin I/F
  • 29. i18n po settings.py Automatic Admin I/F code _(“message”) {% trans “message” %} template
  • 30. i18n po settings.py Automatic Admin I/F po DJANGO_HOME/bin/make-messages.py -l ja DJANGO_HOME/bin/compile-messages.py compile
  • 31. i18n po settings.py Automatic Admin I/F > emacs settings.py MIDDLEWARE_CLASSES = ( 'django.middleware.locale.LocaleMiddleware', 'django.middleware.sessions.SessionMiddleware', 'django.middleware.common.CommonMiddleware', )
  • 32. Python2.3 Apache2.x+mod_python3.x fast-cgi(WSGI) PostgreSQL/MySQL/SQLite3/SQLServer
  • 33. Project Django Model URL view Template
  • 34. Django Django python manage.py init python manage.py startapp APP Project APP APP python manage.py install APP Project django-admin.py startproject PROJECT APP
  • 35. NO django-admin.py startproject PROJECT YES Django python manage.py init python manage.py startapp APP emacs app/APP/models/APP.py python manage.py install APP
  • 36. Project django-admin.py startproject WorkStyle WorkStyle + apps __init__.py __init__.py urls.py settings.py manage.py
  • 37. Project django-admin.py startproject WorkStyle WorkStyle + apps __init__.py __init__.py urls.py settings.py manage.py Project
  • 38. > emacs Project/settings.py # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'. DATABASE_ENGINE = 'postgresql' DATABASE_NAME = 'workstyle' DATABASE_USER = 'workstyle' DATABASE_PASSWORD = 'workstyle' DATABASE_HOST = '' DATABASE_PORT = ''
  • 39. Django Django python manage.py init ## DJANGO CORE TABLE auth_groups auth_groups_permissions auth_messages auth_permissions auth_users auth_users_groups auth_users_user_permissions content_types core_sessions packages sites
  • 40. python manage.py startapp workstyle WorkStyle + apps __init__.py + workstyle __init__.py view.py + models __init__.py workstyles.py __init__.py urls.py settings.py manage.py
  • 41. python manage.py startapp workstyle WorkStyle + apps __init__.py + workstyle __init__.py view.py + models __init__.py workstyles.py __init__.py urls.py settings.py manage.py
  • 42. python manage.py startapp workstyle WorkStyle + apps __init__.py + workstyle __init__.py view.py + models __init__.py workstyles.py __init__.py urls.py settings.py manage.py
  • 43. python manage.py startapp workstyle WorkStyle + apps __init__.py + workstyle __init__.py view.py + models __init__.py workstyles.py __init__.py urls.py settings.py manage.py
  • 44. python manage.py startapp workstyle WorkStyle + apps __init__.py + workstyle __init__.py view.py + models __init__.py workstyles.py __init__.py urls.py settings.py manage.py
  • 45. python manage.py startapp workstyle WorkStyle + apps __init__.py + workstyle __init__.py view.py + models __init__.py workstyles.py __init__.py urls.py settings.py manage.py
  • 46. Model Model > emacs Project/apps/workstyle/models/workstyle.py from django.core import meta class Task(meta.Model): task = meta.TextField(db_index=True) create_date = meta.DateTimeField(auto_now_add=True) update_date = meta.DateTimeField() tag_searchable = meta.CharField(maxlength=800, db_index=True, null=True) estimate = meta.FloatField(max_digits=3, default=0, decimal_places=1, null=True) status = meta.IntegerField(maxlength=1, default=3, choices=TASK_STATUS_CHOICES, db_index=True) class META: ordering = ['-update_date']
  • 47. Model Model meta.Model > emacs Project/apps/workstyle/models/workstyle.py from django.core import meta class Task(meta.Model): task = meta.TextField(db_index=True) create_date = meta.DateTimeField(auto_now_add=True) update_date = meta.DateTimeField() tag_searchable = meta.CharField(maxlength=800, db_index=True, null=True) estimate = meta.FloatField(max_digits=3, default=0, decimal_places=1, null=True) status = meta.IntegerField(maxlength=1, default=3, choices=TASK_STATUS_CHOICES, db_index=True) class META: ordering = ['-update_date']
  • 48. Model python manage.py install workstyle ## APPLICATION TABLE workstyle_tasks workstyle_tags workstyle_taglists workstyle_comments workstyle_attachments
  • 49. Model python manage.py install workstyle ## APPLICATION TABLE workstyle_tasks workstyle_tags workstyle_taglists workstyle_comments workstyle_attachments
  • 50. Model python manage.py install workstyle ## APPLICATION TABLE workstyle_tasks workstyle_tags workstyle_taglists workstyle_comments workstyle_attachments Class
  • 51. O/R Python > export DJANGO_SETTINGS_MODULE=WorkStyle.settings > python
  • 52. O/R Python > export DJANGO_SETTINGS_MODULE=WorkStyle.settings > python import django.models.workstyle import tasks, comments #tasks Task workstyle_tasks #(Black Magic) tasks.get_list(**keyargs) # query = {} query[‘status__in’] = [1,2,3] tasks.get_list(**query)
  • 53. O/R Python > export DJANGO_SETTINGS_MODULE=WorkStyle.settings > python import django.models.workstyle import tasks, comments tsk1 = tasks.get_object(pk=1) #pk tsk1.status = 2 tsk1.save() # tsk2 = tasks.Task(task='hoge',status=2, update_date=datetime.now()) tsk2.save() #
  • 54. O/R Python > export DJANGO_SETTINGS_MODULE=WorkStyle.settings > python import django.models.workstyle import tasks, comments # tsk.add_comment(comment=in_comment_body, commentator=in_commentator) # 1:n tsk.get_comment_list()
  • 55. O/R Python > export DJANGO_SETTINGS_MODULE=WorkStyle.settings > python import django.models.workstyle import tasks, comments #join:Comment # Comment get_task SQL comments.get_list(select_related=True)
  • 56. O/R Python > export DJANGO_SETTINGS_MODULE=WorkStyle.settings > python import django.models.workstyle import tasks, comments #join:Comment # Comment get_task SQL comments.get_list(select_related=True)
  • 57. URL Dispatcher > emacs Project/urls.py (r'^WorkStyle/', include('WorkStyle.apps.workstyle.urls')), > touch Project/apps/workstyle/urls.py > emacs Project/apps/workstyle/urls.py (r'^Task/(?P<task_id>d+)/edit/$', 'WorkStyle.apps.workstyle.task.edit_task'),
  • 58. URL Dispatcher > emacs Project/urls.py (r'^WorkStyle/', include('WorkStyle.apps.workstyle.urls')), > touch Project/apps/workstyle/urls.py > emacs Project/apps/workstyle/urls.py (r'^Task/(?P<task_id>d+)/edit/$', 'WorkStyle.apps.workstyle.task.edit_task'),
  • 59. > emacs WorkStyle/apps/workstyle/task.py def edit_task(request, task_id) : task = get_object_or_404(tasks,pk=task_id) manipulator = tasks.ChangeManiplator() if request.POST : new_data = request.POST.copy() errors = manipulator.get_validation_errors(new_data) if not errors : # return render_to_response(....) else : errors = {} new_data = {'status': str(task.status), 'task': task.task, 'estimate': task.estimate} return render_to_response('workstyle/TaskFormEdit',{'form': form, 'task': task....})
  • 60. > emacs WorkStyle/apps/workstyle/task.py def edit_task(request, task_id) : task = get_object_or_404(tasks,pk=task_id) manipulator = tasks.ChangeManiplator() if request.POST : new_data = request.POST.copy() errors = manipulator.get_validation_errors(new_data) if not errors : # return render_to_response(....) else : errors = {} new_data = {'status': str(task.status), 'task': task.task, 'estimate': task.estimate} return render_to_response('workstyle/TaskFormEdit',{'form': form, 'task': task....})
  • 61. Manipulator model Manipulator (Add,Change) def create(request): manipulator = tasks.AddManipulator() new_data = request.POST.copy() errors = manipulator.get_validation_errors(new_data) if errors : # else : manipulator.do_html2python(request.POST) new_task = manipulator.save(request.POST)
  • 62. Manipulator model Add Change Manipulator (Add,Change) def create(request): manipulator = tasks.AddManipulator() new_data = request.POST.copy() errors = manipulator.get_validation_errors(new_data) if errors : # else : manipulator.do_html2python(request.POST) new_task = manipulator.save(request.POST)
  • 63. Manipulator validation model errors Manipulator (Add,Change) def create(request): manipulator = tasks.AddManipulator() new_data = request.POST.copy() errors = manipulator.get_validation_errors(new_data) if errors : # else : manipulator.do_html2python(request.POST) new_task = manipulator.save(request.POST)
  • 64. Manipulator model Manipulator (Add,Change) def create(request): manipulator = tasks.AddManipulator() new_data = request.POST.copy() errors = manipulator.get_validation_errors(new_data) if errors : # else : manipulator.do_html2python(request.POST) new_task = manipulator.save(request.POST)
  • 65. Manipulator class TaskManipulator(formfields.Manipulator): def __init__(self): self.fields = ( formfields.LargeTextField( field_name="task_tag", validator_list=[self.isValidTagName]), formfields.TextField( field_name="commentator", maxlength=50, is_required=False), formfields.SelectField( field_name="status", choices=TASK_STATUS, is_required=True), ) def isValidTagName(self, field_data, all_data): task_tag_list = string.split(field_data, "]") for task_tag in task_tag_list : task_tag = string.strip(string.replace(task_tag, "[", "")) if len(task_tag) > 49 : raise validators.ValidationError(_("Tag's name is must be less than 50 characters."))
  • 66. Manipulator #view manipulator = TaskManipulator() try : task = tasks.get_object(pk=task_id) except ObjectDoesNotExist : raise Http404 if request.POST : new_data = request.POST.copy() errors = manipulator.get_validation_errors(new_data) if not errors : return update_task(request, task_id) else :
  • 67. Template settings.py Template > emacs Project/settings.py TEMPLATE_DIRS = ( WORKSTYLE_BASE_DIR + "/apps/workstyle/templates", )
  • 68. Template settings.py Template > emacs Project/settings.py TEMPLATE_DIRS = ( WORKSTYLE_BASE_DIR + "/apps/workstyle/templates", ) > emacs Project/apps/workstyle/templates/workstyle/TaskForm.html <div id="main"> {% block formaction %} <form action="{{ workstyle_root }}/Task/{{ task.id }}/update/" name="taskForm" method="POST" enctype="multipart/form-data" class="tableForm" onSubmit="return checkBody();"> {% endblock %} Base
  • 69. Template settings.py Template > emacs Project/settings.py TEMPLATE_DIRS = ( WORKSTYLE_BASE_DIR + "/apps/workstyle/templates", ) > emacs Project/apps/workstyle/templates/workstyle/TaskFormNew.html {% extends "workstyle/TaskForm" %} {%block formaction %} <form action="{{ workstyle_root }}/Task/add/" name="task_form" method="POST" enctype="multipart/form-data" class="tableForm" onSubmit="return checkBody();"> {% endblock %} New extends Base
  • 70. Template settings.py Template > emacs Project/settings.py TEMPLATE_DIRS = ( WORKSTYLE_BASE_DIR + "/apps/workstyle/templates", ) > emacs Project/apps/workstyle/templates/workstyle/TaskFormEdit.html {% extends "workstyle/TaskForm" %} {%block formaction %} <form action="{{ workstyle_root }}/Task/{{ task.id }}/update/" name="taskForm" method="POST" enctype="multipart/form-data" class="tableForm" onSubmit="return checkBody();"> {% endblock %} Edit extends Base
  • 71. > emacs Project/apps/workstyle/templatetags/wsfilter.py from django.core import template register = template.Library() def truncatelines(value, arg) : result = value #do something return result register.filter('truncatelines', truncatelines)
  • 72. > emacs Porject/apps/workstyle/task.py from WorkStyle.apps.workstyle.templatetags import wsfilters
  • 73. > emacs Porject/apps/workstyle/task.py from WorkStyle.apps.workstyle.templatetags import wsfilters > emacs Project/apps/workstyle/templates/workstyle/TaskList.html {% load wsfilters %} {{ task.task|truncatelines:3 }}
  • 74. Test pyunit simon.bofh.ms
  • 75. Django Milestone 0.92 Magic 1.00
  • 76. www.everes.net