Django 1.5 における効果的な MTV 設計 & ネイティブApp

  • 2,970 views
Uploaded on

Django 1.5 における効果的な MTV 設計 & ネイティブApp @ PyCon APAC 2013

Django 1.5 における効果的な MTV 設計 & ネイティブApp @ PyCon APAC 2013

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
2,970
On Slideshare
0
From Embeds
0
Number of Embeds
2

Actions

Shares
Downloads
13
Comments
0
Likes
6

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

Transcript

  • 1. Django 1.5 における効果的な MTV 設計 & ネイティブApp @luyikei
  • 2. 自己紹介 ● @luyikei ● Linux Mint, Qt, Django が好きです
  • 3. 内容 1, DjangoでMTVデザインパターンを最も有効に活用するには  Model : 1, Model Relationships 2, Mata Options  Template : 1, Python Template Engine 2, Custom tags and filters  View : 1, Sessions 2, Cookie 3, Cache    
  • 4. 内容 2,ネイティブアプリケーションとの連携  API の実装  OAuth の実装  クライアント側の処理 (PyQt 使用)  
  • 5. MTV デザインパターンを最も有効に活用するには
  • 6. MTV とは Django appears to be a MVC framework, but you call the Controller the “view”, and the View the “template”. How come you don’t use the standard names?¶ (https://docs.djangoproject.com/en/dev/faq/general/ より)
  • 7. なぜ MVC じゃなく MTV なの? さらに、テンプレートによってコンテンツとプレゼンテーションの分離がはっ きり しています。Django では、ビューはどのデータを提示するかを決めてい ますが、 ビューは通常、 どのように データを提示するかをテンプレートに委 ねます。 では、「コントローラ」はどこに入るのでしょうか。 Django の場合、おそら くフ レームワーク、すなわち URL 設定にしたがってリクエストを適切な ビューに送信す る機構自体がコントローラにあたるといえるでしょう。 ( http://www.djangoproject.jp/doc/ja/1.0/faq/general.html )
  • 8. つまり MVC とは少し違ったデザインパターンで ある ということである
  • 9. Model : 1, Model Relationships 簡易 商品管理 サイトを構築する。 - モデルは 「サンドイッチ, おにぎり」 - 一括で管理したい場合はどうするか
  • 10. Model - サンドイッチ Title : “シャキシャキレタス” Price : 220 Vegetable : “Lettuce” Meat : “Ham” OnEgg : True
  • 11. Model - おにぎり Title : “手巻おにぎり 紅しゃけ” Price : 136 Guzai : “Salmon”
  • 12. モデルの継承を使用 class Item(models.Model): title = models.CharField(max_length=200) price = models.IntegerField()
  • 13. Model “サンドイッチ”を定義 class Sandwich(Item): vegetable = models.CharField(max_length=200) meat = models.CharField(max_length=200) onEgg = models.BooleanField(default=False)
  • 14. Model “おにぎり” を定義 class Onigiri(Item): Guzai = models.CharField(max_length=200)
  • 15. Item の子モデルの取得 Onigiri の場合。 Item.onigiri Sandwich の場合 Item.sandwich
  • 16. 子モデルは種類がわかっている時のみ取得でき る!
  • 17. 子モデルを取得する方法 class Item(models.Model): title = models.CharField(max_length=200) price = models.IntegerField() subclass = models.CharField(max_length=200,editable=False) def save(self, *args, **kwargs): # save what kind we are. self.subclass = self.__class__.__name__ super(Item, self).save(*args, **kwargs)
  • 18. 子モデル取得関数 def as_child(self): return getattr(self, self.subclass.lower())
  • 19. 例: i=Item.objects.get(id=1) child=i.as_child()
  • 20. どう動くか見てみましょう
  • 21. Model : 2, Mata Options Django のモデルの情報を取得するのに Meta オプションは重宝します
  • 22. Django Models Internals Documentation https://django-model-_meta- reference.readthedocs.org/en/latest/index.html
  • 23. get_all_related_objects_with_model Returns a list of (related-object, model) pairs. Similar to get_fields_with_model(). Related オブジェクトのペアのリストを返します 例: [(<RelatedObject: common:sandwich related to item_ptr>, None), (<RelatedObject: common:onigiri related to item_ptr>, None)]
  • 24. get_parent_list Returns a list of all the ancestor of this model as a list. Useful for determining if something is an ancestor, regardless of lineage. 全てのこのモデルの親(先祖)のリストを返します (私の実機では list ではなく set オブジェクトでした) 例: set([<class 'common.models.Item'>])
  • 25. get_latest_by model Manager の latest() , earliest() メソッドで返さ れるオブジェクトの判断基準を設定 例: get_latest_by = "order_date"
  • 26. ordering オブジェクトのリストを取得するときに使われ る、オブジェクトのデフォルトの並び順規則を設 定 例: ordering = ['-order_date']
  • 27. db_table モデルの使うデータベーステーブルの名前 例: db_table = 'music_album'
  • 28. Template : 1, Python Template Engine ・Django Template ・Jinja2 ・Mako ・Chameleon
  • 29. Django Template ・Django 標準のテンプレートエンジン ・Django のテンプレート言語は、釣合いの取れたパワーと簡便さを実現するように、 また HTML を扱い なれた人にとっては快適になるように設計されています。 Smarty や CheetahTemplate のようなテキスト ベースのテンプレート言語を経験 したことがあるなら、 Django のテンプレートはしっくりくるはずで す。 ・プログラミングの知識があったり、 PHP のようなプログラムコードを直接 HTML に混ぜ込む言語を 使ったことがあるなら、 Django のテンプレートシステ ムが単に HTML に Python を埋め込んだものでない ことに疑問を持つでしょう。 しかし、これは設計上意図して決まっていることです。テンプレートシステ ム はプレゼンテーション層を表現するためのもので、プログラムロジックではな いのです。 ・http://docs.djangoproject.jp/en/latest/topics/templates.html
  • 30. 簡潔で可読性の高いコード Test[“a”] → {{ Test.a }} Test[“a”][“b”] → {{ Test.a.b }} Test[a].b() → {{ Test.a.b }} Test.a.b → {{ Test.a.b }}
  • 31. Jinja 2 ・Django Template のパワーアップ版 ・書式はほぼ同じなので移植しやすい ・高速 ・おすすめ!
  • 32. Django Template との比較 ・Method Calls 例: a.b() → {{ a.b() }} (必ず) a[“b”] → {{ a[“b“] }} (任意) ・Conditions 例: {% ifequal a b %} が無効。 代わりに {% if a == b%} を使用。 ・Filter Arguments 例: {{ items|join:", " }} → {{ items|join(', ') }}
  • 33. Mako <%inherit file="base.html"/> <% rows = [[v for v in range(0,10)] for row in range(0,10)] %> <table> % for row in rows: ${makerow(row)} % endfor </table> <%def name="makerow(row)"> <tr> % for name in row: <td>${name}</td> % endfor </tr> </%def>
  • 34. Mako = 高機能 先ほどのコードを見てみると Django Template では とてもではないが表現できるものではないことがわかる。 コードの可読性は Django より劣る傾向にあるだろう しかし表現の幅は広がるだろう
  • 35. 正確で厳しいコード Test[“a”] → ${ Test[“a”] } Test[“a”][“b”] → ${ Test[“a”][“b”] } Test[a].b() → ${ Test[a].b() } Test.a.b → ${ Test.a.b }
  • 36. Chameleon <html> <meta> <title tal:content="context.title" /> </meta> <body> <div tal:condition="items"> <p>These are your items:</p> <ul> <li tal:repeat="item items" tal:content="item" /> </ul> </div> </body> </html>
  • 37. Chameleon TAL≒ ・TAL とは Zope のテンプレートエンジン ・Chameleon はそれをベースに改良したテンプ レートエンジン ・XMLベース
  • 38. Django の XML ベーステンプレートエンジンに対する 評価 ① 「それは、 Django のテンプレート言語を XML/HTML テンプレート 以外にも使いたいと考えているからです。 」 Django テンプレートエンジンについて 「World Online では、email や JavaScript、 CSV にテンプレートを 使っています。テンプレートはテキストベースの形式なら何にでも 使 えるのです。」 http://docs.djangoproject.jp/ja/latest/topics/templates.html
  • 39. まず・・・ 例: polls/ models.py templatetags/ __init__.py poll_extras.py views.py カスタムテンプレートタグ・フィルターは Django アプリケーションのフォルダ内の templatetags フォルダ内のファイルに記述 https://docs.djangoproject.com/en/dev/howto/custom-template-tags/
  • 40. Django の XML ベーステンプレートエンジンに対する 評価 ② そう、もう一つあります: 人間に XML を編集させるなんて、 サディスティック でしかありません! http://docs.djangoproject.jp/ja/latest/topics/templates.html
  • 41. Template : 2, Custom tags and filters 今回は Django Template において説明します
  • 42. django/template/defaultfilters.py from django.template.base import Library register = Library() @register.filter(is_safe=True) @stringfilter def lower(value): """Converts a string into all lowercase.""" return value.lower()
  • 43. Django のソースを読むとわかる! テンプレートのフィルターの書き方は Python の 関数 の書き方と同じ!
  • 44. まず・・・ 例: polls/ models.py templatetags/ __init__.py poll_extras.py views.py カスタムテンプレートタグ・フィルターは Django アプリケーションのフォルダ内の templatetags フォルダ内のファイルに記述 https://docs.djangoproject.com/en/dev/howto/custom-template-tags/
  • 45. フィルターを登録 例: from django.template.base import Library register = Library() @register.filter() def nothing(value): pass フィルターを登録し完了
  • 46. タグの場合も同じく 例: from django.template.base import Library register = Library() @register.tag def nothing(value): pass タグを登録し完了
  • 47. View : 1, Sessions
  • 48. セッションとは ユーザがあるサイトを訪れてからそこを離れるまでの一連の 通信。 HTTP自体にはユーザを識別する機能が存在しないの で、Cookieなどを使用して同一人物か否かを識別する。 たとえば、はてなにログインしてからログアウトするまでの すべての操作は一つのセッションと考えられる。 http://d.hatena.ne.jp/keyword/%A5%BB %A5%C3%A5%B7%A5%E7%A5%F3
  • 49. 変数の保存 例: request.session[“Height”] = 120
  • 50. 変数の取得 例: request.session.get('Height', 0): get(key, default=None) 例: fav_color = request.session.get('fav_color', 'red')
  • 51. 変数の削除 例: del request.session[“Height”]
  • 52. 簡単!
  • 53. View : 2, Cookie
  • 54. クッキーの保存 HttpResponse.set_cookie(key, value='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False) 例: response = render_to_response(template_name, context) response.set_cookie('Height', ') return response HttpResponse.set_cookie であることに注意
  • 55. クッキーの取得 例: request.COOKIES.get('Height')
  • 56. 簡単!
  • 57. View : 3, Cache
  • 58. キャッシュとは 使用頻度の高いデータを高速な記憶装置に蓄えておくことに より、いちいち低速な装置から読み出す無駄を省いて高速化 すること。また、その際に使われる高速な記憶装置や、複製 されたデータそのもののこと。 http://e-words.jp/w/E382ADE383A3E38383E382B7E383A5.html
  • 59. キャッシュの設定 Memcached データベースを使ったキャッシュ データベースを使ったキャッシュと、マルチデータ ベース ファイルシステムを使ったキャッシュ ローカルメモリ上のキャッシュ ダミーキャッシュ (開発用)
  • 60. 例:Memcached settings.py に CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedC ache', 'LOCATION': '127.0.0.1:11211', } }
  • 61. サイト単位のキャッシュ MIDDLEWARE_CLASSES = ( 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', ) http://docs.djangoproject.jp/en/latest/topics/cache.html#id10
  • 62. ビュー単位のキャッシュ from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request): … 又は from django.views.decorators.cache import cache_page urlpatterns = ('', (r'^foo/(d{1,2})/$', cache_page(60 * 15)(my_view)), ) http://docs.djangoproject.jp/en/latest/topics/cache.html#id10
  • 63. テンプレートの部分的キャッシュ {% load cache %} {% cache 500 sidebar %} .. sidebar .. {% endcache %} http://docs.djangoproject.jp/en/latest/topics/cache.html#id10
  • 64. 2,ネイティブアプリケーションとの 連携
  • 65. ネイティブアプリケーション ネイティブアプリケーションとの連携について 実際にはクライアントの作成に等しいが、 Django の ネイティブアプリケーション のクライ アントの作成事例を中々見かけないのでこの題に 設定した。 Qt を使用し実装してみる
  • 66. まずは OAuth の説明 OAuth とは・・・ ・「OAuth (オー オース[1]) は、ブレイン・クックとクリス・メッ シーナが始めたオープンプロトコルであり、デスクトップ、モバ イル、WebアプリケーションなどにセキュアなAPI認可 (authorization) の標準的手段を提供する。」(Wikipedia) ・要は API 用の認証手段である
  • 67. 詳しい説明 ● http://www.atmarkit.co.jp/fsecurity/rensai/digid01/02.htm
  • 68. 準備 ● Django の OAuth2 実装に django-oauth-plus を使用。 ● Django の API 実装に django-tastypie を使用。
  • 69. INSTALLED_APPS に以下を追記 INSTALLED_APPS = ( 'tastypie', 'oauth_provider', )
  • 70. まずは API を実装 common/api.py from tastypie.resources import ModelResource from common.models import Onigiri class OnigiriResource(ModelResource): class Meta: queryset = Onigiri.objects.all() resource_name = 'onigiri
  • 71. Urlsの編集 ecsite/urls.py from tastypie.api import Api from django.contrib import admin admin.autodiscover() onigiri_resource = OnigiriResource() v1_api = Api(api_name='v1') v1_api.register(OnigiriResource()) urlpatterns = patterns('', url(r'^api/', include(onigiri_resource.urls)), )
  • 72. 完成!(例) $ curl http://127.0.0.1:8000/api/v1/onigiri/?format=json {"meta": {"limit": 20, "next": null, "offset": 0, "previous": null, "total_count": 1}, "objects": [{"guzai": "gre", "id": 1, "price": 436, "resource_uri": "/admin/v1/onigiri/1/", "subclass": "Onigiri", "title": "sdg"}]}
  • 73. しかしこの状態では However, if you try sending a POST/PUT/DELETE to the resource, you find yourself getting “401 Unauthorized” errors. For safety, Tastypie ships with the authorization class (“what are you allowed to do”) set to ReadOnlyAuthorization. This makes it safe to expose on the web, but prevents us from doing POST/PUT/DELETE. Let’s enable those: http://django-tastypie.readthedocs.org/en/latest/tutorial.html POST/PUT/DELETE が使用できない → つまり API として利用するには取得しか出来ない!
  • 74. 認証・権限の実装 common/api.py from tastypie.authentication import OAuthAuthentication from tastypie.authorization import DjangoAuthorization from tastypie.resources import ModelResource from common.models import Onigiri class OnigiriResource(ModelResource): class Meta: queryset = Onigiri.objects.all() resource_name = 'onigiri authentication = OAuthAuthentication() authorization = DjangoAuthorization
  • 75. 同じコマンドを実行 $ curl http ://127.0.0.1:8000/api/v1/onigiri/?format=json Invalid request parameters.
  • 76. OAuth実装完了!
  • 77. 検証: Consumer を作成 $ ./manage.py shell >>> from oauth_provider.models import Consumer, Token >>> c = Consumer() >>> c.generate_random_codes() >>> c <Consumer: Consumer with key b10945ea448344bb8d2c7f33d38f2f0d> >>> c.key u'b10945ea448344bb8d2c7f33d38f2f0d' >>> c.secret U'AQsJnvVYn04njIVK' http://d.hatena.ne.jp/yuheiomori0718/20120924/1348496794
  • 78. 検証: Token を作成 $ ./manage.py shell >>> from django.contrib.auth.models import User >>> u = User.objects.all()[0] >>> t = Token() >>> t.user = u >>> t.consumer = c >>> t.token_type = 2 >>> t.resource_id=0 >>> t.generate_random_codes() >>> t.key 'bec4fdc33725458ab2894558014844b7' >>> t.secret U'WgKrSb7QMz9CLZyX' http://d.hatena.ne.jp/yuheiomori0718/20120924/1348496794
  • 79. ここでこのコマンドを実行 # coding=utf-8 import json import requests from oauth_hook import OAuthHook consumer_key = 'b10945ea448344bb8d2c7f33d38f2f0d' consumer_secret = 'AQsJnvVYn04njIVK' access_token = 'bec4fdc33725458ab2894558014844b7' access_token_secret = 'WgKrSb7QMz9CLZyX' oauth_hook = OAuthHook(access_token=access_token, access_token_secret=access_token_secret, consumer_key=consumer_key, consumer_secret=consumer_secret, header_auth=True) request = requests.Request('GET', "http://127.0.0.1:8000/api/v1/onigiri/?format=json") request = oauth_hook(request) prepared = request.prepare() session = requests.session() resp = session.send(prepared) print resp.text
  • 80. 結果は {"meta": {"limit": 20, "next": null, "offset": 0, "previous": null, "total_count": 1}, "objects": [{"guzai": "gre", "id": 1, "price": 436, "resource_uri": "/api/v1/onigiri/1/", "subclass": "Onigiri", "title": "sdg"}]}
  • 81. PyQt で実装
  • 82. おわり!