Djangoで黒魔術
@whosaysni
自己紹介
増田 泰 (@whosaysni)!
http://whosaysni.jp/!
!
某バイオベンチャー勤務ですが…!
!
一般社団法人PyConJP理事!
PyConJP 9/13-15 CFP中!
http://2014.pycon.jp/!
!
ろうじん老神.py 幹事!
https://sites.google.com/site/oikamipy/
Django
Django
もうすぐ9歳!
0.95 Djangoドキュメントが和訳されるw
0.98 内部コードがUnicode化される
1.0 メジャーリリース
1.2 マルチDBに対応
1.3 クラスベースビュー、さようならmod_python
1.4 project/app のレイアウトが変更される
1.6 Userを置き換えられるようになった(らしい)
1.7 スキーママイグレーションが組み込まれる(予定)
おしながき
• {% with %} タグの変態的な誤った使い方
• DjangoのマルチDBをハックする
{% with %}
{% with %} タグ
• テンプレート中で変数を束縛する
• ブロック内がスコープ
• スコープ内の {% include %} に波及
する
• ネスト可
{%	 with	 a='foo'	 b='bar'	 %}

!
Outside	 nest:{{	 a	 }}/{{	 b	 }}/{{	 c	 }}.

!
{%	 with	 c=a	 b='baz'	 %}

!
Inside	 nest:	 {{	 a	 }}/{{	 b	 }}/{{	 c	 }}.

!
{%	 endwith	 %}

!
{%	 endwith	 %}
!
Outside	 nest:	 foo/bar/.

!
Inside	 nest:	 foo/baz/foo.
{% with %} の構文
{%	 with	 a=123	 c='defg'	 e=hij	 ...	 %}
タグ トークン列
トークン トークン トークン
a=123
a	 as	 123
古い形式の
キーワード引数
キーワード引数
キー 引数
{% with %} の実装(1)
@register.tag('with')

def	 do_with(parser,	 token):

	 	 	 	 ...

	 	 	 	 bits	 =	 token.split_contents()

	 	 	 	 remaining_bits	 =	 bits[1:]

	 	 	 	 extra_context	 =	 token_kwargs(remaining_bits,	 parser,	 support_legacy=True)

	 	 	 	 if	 not	 extra_context:

	 	 	 	 	 	 	 	 raise	 TemplateSyntaxError("%r	 expected	 at	 least	 one	 variable	 "

	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 "assignment"	 %	 bits[0])

	 	 	 	 if	 remaining_bits:

	 	 	 	 	 	 	 	 raise	 TemplateSyntaxError("%r	 received	 an	 invalid	 token:	 %r"	 %

	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 (bits[0],	 remaining_bits[0]))

	 	 	 	 nodelist	 =	 parser.parse(('endwith',))

	 	 	 	 parser.delete_first_token()

	 	 	 	 return	 WithNode(None,	 None,	 nodelist,	 extra_context=extra_context)

トークン列を取り出す
テンプレートの解析中に with タグを見つけたときの処理
各トークンをキーワード引数
として解析する
レンダリングノードを作成する
endwith までを子ノードにする
{% with %} の実装(2)
class	 WithNode(Node):

	 	 	 	 def	 __init__(self,	 var,	 name,	 nodelist,	 extra_context=None):

	 	 	 	 	 	 	 	 self.nodelist	 =	 nodelist

	 	 	 	 	 	 	 	 #	 var	 and	 name	 are	 legacy	 attributes,	 being	 left	 in	 case	 they	 are	 used

	 	 	 	 	 	 	 	 #	 by	 third-party	 subclasses	 of	 this	 Node.

	 	 	 	 	 	 	 	 self.extra_context	 =	 extra_context	 or	 {}

	 	 	 	 	 	 	 	 if	 name:

	 	 	 	 	 	 	 	 	 	 	 	 self.extra_context[name]	 =	 var

!
	 	 	 	 def	 __repr__(self):

	 	 	 	 	 	 	 	 return	 "<WithNode>"

!
	 	 	 	 def	 render(self,	 context):

	 	 	 	 	 	 	 	 values	 =	 dict([(key,	 val.resolve(context))	 for	 key,	 val	 in

	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 six.iteritems(self.extra_context)])

	 	 	 	 	 	 	 	 context.update(values)

	 	 	 	 	 	 	 	 output	 =	 self.nodelist.render(context)

	 	 	 	 	 	 	 	 context.pop()

	 	 	 	 	 	 	 	 return	 output

子ノード(ブロックの内容)
キーワード引数
互換性用のコード
キーワード引数の値を解決して辞書にする
子ノードをレンダリング
コンテキストをスタック
コンテキストを復帰
コンテキスト
• インタフェースは辞書
• データ構造は辞書のスタック
• 一番上から順に検索する
context['color'] -> 'blue'
context['number'] -> 2
context['food'] -> 'spam'
Context
number=42
food='spam'
animal='duck'
color='blue'
number=2
dict
dict
dict
• 要するに、 {% with %} タグは、一時的にコンテ
キストに辞書を積んでブロックをレンダするタグ
• スコープの外の同名の変数は見えなくなる
• with のキーワード引数は、ブロックのレンダ直
前に解決される
ということで・・・
変#1: デフォルト値
• コンテキストに username が
あるかどうかわからない
• username がなければ「名無
し」さん
• username があればその値
{{	 username	 }}	 	 	 これはダメ
{{	 username|default:'名無し'	 }}	 	 	 一応OK
<span>ようこそ{{	 username|default:'名無
し'	 }}さん</span>

!
...

!
<h1>{{	 username|default:'名無し'	 }}さん
のプロフィール</h1>

!
...

!
<h2>{{	 username|default:'名無し'	 }}さん
へのおすすめ</h2>

!
....

(#`Д́)ノノ┻┻;:'、・゙ヤッテラレルカ!
変#1: デフォルト値
{%	 with	 username=username|default:'名無し'	 %}

!
<span>ようこそ{{	 username	 }}さん</span>

!
...

!
<h1>{{	 username	 }}さんのプロフィール</h1>

!
...

!
<h2>{{	 username	 }}さんへのおすすめ</h2>

!
....

!
{%	 endwith	 %}
• コンテキスト変数を確実に埋
めておきたいときは、{% with
%}でデフォルト値をオーバラ
イドしたブロックをつくる
• 同じ変数を何度も参照したい
ときにも、 {% with %} が有
効
変#2: {% include %}の悪用
Hello	 {{	 foo	 }}.
• {% include %} は、レンダ時
にテンプレートを展開する(実
際には、include 対象をレン
ダして結果を挿入する)
• レンダ対象にはコンテキスト
がそのまま渡る
{%	 with	 foo='bar'	 %}

!
{%	 include	 "included.html"	 %}

!
{%	 endwith	 %}
Hello	 bar.
変#2: {% include %}の利用
{{	 field.label_tag	 }}

<p	 class="tertiary-text-secondary">

	 	 {{	 field.help_text	 }}

</p>

<div>

	 	 {%	 with	 field_type=type|default:""	 %}

	 	 <div	 class="input-control"	 ...>

	 	 	 	 {%	 ifequal	 field_type	 ""	 %}

	 	 	 	 {{	 field	 }}

	 	 	 	 {%	 endifequal	 %}

	 	 	 	 {%	 ifequal	 field_type	 "text"	 %}

	 	 	 	 {{	 field	 }}

	 	 	 	 <button	 type="button"	 class="btn-clear"></button>

	 	 	 	 {%	 endifequal	 %}

	 	 	 	 {%	 ifequal	 field_type	 "password"	 %}

	 	 	 	 {{	 field	 }}

	 	 	 	 <button	 type="button"	 	 class="btn-reveal"></button>

	 	 	 	 {%	 endifequal	 %}

	 	 	 	 {%	 ifequal	 field_type	 "switch"	 %}

	 	 	 	 <label>

	 	 	 	 	 	 {{	 field	 }}

	 	 	 	 	 	 <span	 class="check"></span>

	 	 	 	 </label>

	 	 	 	 {%	 endifequal	 %}

	 	 	 	 <span	 class="tertiary-text	 text-alert">

	 	 	 	 	 	 {{	 field.errors.as_text	 }}

	 	 	 	 </span>

	 	 </div>

	 	 {%	 endwith	 %}

</div>
• フォームフィールドをカスタ
マイズしたいとき
• 正攻法は widget のサブクラ
ス化(めんどくさい)
• 各フィールドタイプで分岐す
るテンプレートを作っておく
field.html
変#2: {% include %}の利用
<div	 class="container	 padding10">

	 	 <h1>Add	 user</h1>

	 	 <div	 class="padding10">

	 	 	 	 <form	 method="POST"	 action="{%	 url	 'user_add'	 %}">

	 	 	 	 	 	 {%	 csrf_token	 %}

	 	 	 	 	 	 <fieldset>

	 	 	 	 	 	 	 	 {%	 with	 field=form.is_superuser	 type='switch'	 %}{%	 include	 "field.html"	 %}{%	 endwith	 %}

	 	 	 	 	 	 	 	 {%	 with	 field=form.is_active	 type='switch'	 %}{%	 include	 "field.html"	 %}{%	 endwith	 %}

	 	 	 	 	 	 	 	 {%	 with	 field=form.username	 type='text'	 %}{%	 include	 "field.html"	 %}{%	 endwith	 %}

	 	 	 	 	 	 	 	 {%	 with	 field=form.password	 type='password'	 %}{%	 include	 "field.html"	 %}{%	 endwith	 %}

	 	 	 	 	 	 	 	 {%	 with	 field=form.email	 type='text'	 %}{%	 include	 "field.html"	 %}{%	 endwith	 %}

	 	 	 	 	 	 	 	 {%	 with	 field=form.first_name	 type='text'	 %}{%	 include	 "field.html"	 %}{%	 endwith	 %}

	 	 	 	 	 	 	 	 {%	 with	 field=form.last_name	 type='text'	 %}{%	 include	 "field.html"	 %}{%	 endwith	 %}

	 	 	 	 	 	 </fieldset>

	 	 	 	 	 	 <div	 class="button-group">

	  <input	 type="submit"	 value="Add"	 />

	 	 	 	 	 	 </div>

	 	 	 	 </form>

	 	 </div>

</div>
テンプレート2つで、フィールドをカスタマイズできる
Multi-DB
Django Multi-DB
• 1.2で導入された
• バックエンドDBのマスタ
スレーブ化、シャーディン
グ、レプリケーションを可
能にする
• モデル単位でDBのルーティ
ングができる
Django
App
Replicate/Partition
RO
Access
RW
Access
Multi-DBの構成
• settings に静的に定義
• 名前で区別する
• DBのパラメタは辞書
• using() またはデータベー
スルータで制御する
BE_PG	 =	 'django.db.backends.postgresql_psycopg2'

BE_MY	 =	 'django.db.backends.mysql'

DATABASES	 =	 {

	 	 	 	 'default':	 {

	 	 	 	 	 	 	 	 'NAME':	 'master_db',

	 	 	 	 	 	 	 	 'ENGINE':	 BE_PG,

	 	 	 	 	 	 	 	 'USER':	 'master_user',

	 	 	 	 	 	 	 	 'PASSWORD':	 'boo	 hoo	 woo'

	 	 	 	 },

	 	 	 	 'replicon':	 {

	 	 	 	 	 	 	 	 'NAME':	 'replicon_db',

	 	 	 	 	 	 	 	 'ENGINE':	 BE_MY,

	 	 	 	 	 	 	 	 'USER':	 'replicon_user',

	 	 	 	 	 	 	 	 'PASSWORD':	 'let	 it	 go'

	 	 	 	 }

}
#	 名前でデータベースを指定する

User.objects.using('users').get(...)

!
#	 データベースルータを使う

class	 MyDbRouter(object):

	 	 	 	 def	 db_for_read(self,	 model,	 **kw):

	 	 	 	 	 	 	 	 if	 ...:

	 	 	 	 	 	 	 	 	 	 	 return	 'replicon'

DATABASE_ROUTERS	 =	 ['foo.bar.MyDbRouter',...]
Multi-DBの実装
• データベース名は文字列
• django.db.connections
を使ってDB接続を取り出
している
• connections は辞書ライ
クなインタフェースを持つ
ConnectionHandlerイン
スタンス
from	
 django.db	
 import	
 connections	
 
!
class	
 QuerySet(object):	
 
	
 	
 	
 	
 """	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 
	
 	
 	
 	
 Represents	
 a	
 lazy	
 database	
 lookup	
 for	
 a	
 set	
 of	
 objects.	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 
	
 	
 	
 	
 """	
 
	
 	
 	
 	
 def	
 __init__(self,	
 model=None,	
 query=None,	
 using=None):	
 
	
 	
 	
 	
 	
 	
 	
 	
 self.model	
 =	
 model	
 
	
 	
 	
 	
 	
 	
 	
 	
 self._db	
 =	
 using	
 
	
 	
 	
 	
 	
 	
 	
 	
 self.query	
 =	
 query	
 or	
 sql.Query(self.model)	
 
	
 	
 	
 	
 	
 	
 	
 	
 ...	
 
!
	
 	
 	
 	
 def	
 ...	
 
	
 	
 	
 	
 	
 	
 	
 	
 connection	
 =	
 connections[self.db]	
 
django.db.models.query
from	
 django.db.utils	
 import	
 ConnectionHandler	
 
connections	
 =	
 ConnectionHandler()	
 
django.db.__init__
Multi-DBの実装
• ConnectionHandlerはDB
設定とコネクションプール
をキャッシュしているらし
い
• __getitem__ で何かゴニョ
ゴニョして取り出している
模様
class	
 ConnectionHandler(object):	
 
	
 	
 	
 	
 def	
 __init__(self,	
 databases=None):	
 
	
 	
 	
 	
 	
 	
 	
 	
 self._databases	
 =	
 databases	
 
	
 	
 	
 	
 	
 	
 	
 	
 self._connections	
 =	
 local()	
 
!
	
 	
 	
 	
 ...	
 
!
	
 	
 	
 	
 def	
 __getitem__(self,	
 alias):	
 
	
 	
 	
 	
 	
 	
 	
 	
 if	
 hasattr(self._connections,	
 alias):	
 
	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 return	
 getattr(self._connections,	
 alias)	
 
!
	
 	
 	
 	
 	
 	
 	
 	
 self.ensure_defaults(alias)	
 
	
 	
 	
 	
 	
 	
 	
 	
 db	
 =	
 self.databases[alias]	
 
	
 	
 	
 	
 	
 	
 	
 	
 backend	
 =	
 load_backend(db['ENGINE'])	
 
	
 	
 	
 	
 	
 	
 	
 	
 conn	
 =	
 backend.DatabaseWrapper(db,	
 alias)	
 
	
 	
 	
 	
 	
 	
 	
 	
 setattr(self._connections,	
 alias,	
 conn)	
 
	
 	
 	
 	
 	
 	
 	
 	
 return	
 conn	
 
django.db.utils
• と、いうことは、ConnectionHandlerの実装を
動的に差し替えてやれば、ランタイムでDBの設
定を切り替えられるはず
• DBの設定はPythonオブジェクト→Djangoのモ
デルにできるはず
やってみました
https://gist.github.com/whosaysni/11361218
Thanks!

Django boodoo

  • 1.
  • 2.
    自己紹介 増田 泰 (@whosaysni)! http://whosaysni.jp/! ! 某バイオベンチャー勤務ですが…! ! 一般社団法人PyConJP理事! PyConJP9/13-15 CFP中! http://2014.pycon.jp/! ! ろうじん老神.py 幹事! https://sites.google.com/site/oikamipy/
  • 3.
  • 4.
  • 5.
  • 6.
    0.95 Djangoドキュメントが和訳されるw 0.98 内部コードがUnicode化される 1.0メジャーリリース 1.2 マルチDBに対応 1.3 クラスベースビュー、さようならmod_python 1.4 project/app のレイアウトが変更される 1.6 Userを置き換えられるようになった(らしい) 1.7 スキーママイグレーションが組み込まれる(予定)
  • 7.
    おしながき • {% with%} タグの変態的な誤った使い方 • DjangoのマルチDBをハックする
  • 8.
  • 9.
    {% with %}タグ • テンプレート中で変数を束縛する • ブロック内がスコープ • スコープ内の {% include %} に波及 する • ネスト可 {% with a='foo' b='bar' %} ! Outside nest:{{ a }}/{{ b }}/{{ c }}. ! {% with c=a b='baz' %} ! Inside nest: {{ a }}/{{ b }}/{{ c }}. ! {% endwith %} ! {% endwith %} ! Outside nest: foo/bar/. ! Inside nest: foo/baz/foo.
  • 10.
    {% with %}の構文 {% with a=123 c='defg' e=hij ... %} タグ トークン列 トークン トークン トークン a=123 a as 123 古い形式の キーワード引数 キーワード引数 キー 引数
  • 11.
    {% with %}の実装(1) @register.tag('with') def do_with(parser, token): ... bits = token.split_contents() remaining_bits = bits[1:] extra_context = token_kwargs(remaining_bits, parser, support_legacy=True) if not extra_context: raise TemplateSyntaxError("%r expected at least one variable " "assignment" % bits[0]) if remaining_bits: raise TemplateSyntaxError("%r received an invalid token: %r" % (bits[0], remaining_bits[0])) nodelist = parser.parse(('endwith',)) parser.delete_first_token() return WithNode(None, None, nodelist, extra_context=extra_context) トークン列を取り出す テンプレートの解析中に with タグを見つけたときの処理 各トークンをキーワード引数 として解析する レンダリングノードを作成する endwith までを子ノードにする
  • 12.
    {% with %}の実装(2) class WithNode(Node): def __init__(self, var, name, nodelist, extra_context=None): self.nodelist = nodelist # var and name are legacy attributes, being left in case they are used # by third-party subclasses of this Node. self.extra_context = extra_context or {} if name: self.extra_context[name] = var ! def __repr__(self): return "<WithNode>" ! def render(self, context): values = dict([(key, val.resolve(context)) for key, val in six.iteritems(self.extra_context)]) context.update(values) output = self.nodelist.render(context) context.pop() return output 子ノード(ブロックの内容) キーワード引数 互換性用のコード キーワード引数の値を解決して辞書にする 子ノードをレンダリング コンテキストをスタック コンテキストを復帰
  • 13.
    コンテキスト • インタフェースは辞書 • データ構造は辞書のスタック •一番上から順に検索する context['color'] -> 'blue' context['number'] -> 2 context['food'] -> 'spam' Context number=42 food='spam' animal='duck' color='blue' number=2 dict dict dict
  • 14.
    • 要するに、 {%with %} タグは、一時的にコンテ キストに辞書を積んでブロックをレンダするタグ • スコープの外の同名の変数は見えなくなる • with のキーワード引数は、ブロックのレンダ直 前に解決される
  • 15.
  • 16.
    変#1: デフォルト値 • コンテキストにusername が あるかどうかわからない • username がなければ「名無 し」さん • username があればその値 {{ username }} これはダメ {{ username|default:'名無し' }} 一応OK <span>ようこそ{{ username|default:'名無 し' }}さん</span> ! ... ! <h1>{{ username|default:'名無し' }}さん のプロフィール</h1> ! ... ! <h2>{{ username|default:'名無し' }}さん へのおすすめ</h2> ! .... (#`Д́)ノノ┻┻;:'、・゙ヤッテラレルカ!
  • 17.
    変#1: デフォルト値 {% with username=username|default:'名無し' %} ! <span>ようこそ{{ username }}さん</span> ! ... ! <h1>{{ username }}さんのプロフィール</h1> ! ... ! <h2>{{ username }}さんへのおすすめ</h2> ! .... ! {% endwith %} • コンテキスト変数を確実に埋 めておきたいときは、{% with %}でデフォルト値をオーバラ イドしたブロックをつくる • 同じ変数を何度も参照したい ときにも、 {% with %} が有 効
  • 18.
    変#2: {% include%}の悪用 Hello {{ foo }}. • {% include %} は、レンダ時 にテンプレートを展開する(実 際には、include 対象をレン ダして結果を挿入する) • レンダ対象にはコンテキスト がそのまま渡る {% with foo='bar' %} ! {% include "included.html" %} ! {% endwith %} Hello bar.
  • 19.
    変#2: {% include%}の利用 {{ field.label_tag }} <p class="tertiary-text-secondary"> {{ field.help_text }} </p> <div> {% with field_type=type|default:"" %} <div class="input-control" ...> {% ifequal field_type "" %} {{ field }} {% endifequal %} {% ifequal field_type "text" %} {{ field }} <button type="button" class="btn-clear"></button> {% endifequal %} {% ifequal field_type "password" %} {{ field }} <button type="button" class="btn-reveal"></button> {% endifequal %} {% ifequal field_type "switch" %} <label> {{ field }} <span class="check"></span> </label> {% endifequal %} <span class="tertiary-text text-alert"> {{ field.errors.as_text }} </span> </div> {% endwith %} </div> • フォームフィールドをカスタ マイズしたいとき • 正攻法は widget のサブクラ ス化(めんどくさい) • 各フィールドタイプで分岐す るテンプレートを作っておく field.html
  • 20.
    変#2: {% include%}の利用 <div class="container padding10"> <h1>Add user</h1> <div class="padding10"> <form method="POST" action="{% url 'user_add' %}"> {% csrf_token %} <fieldset> {% with field=form.is_superuser type='switch' %}{% include "field.html" %}{% endwith %} {% with field=form.is_active type='switch' %}{% include "field.html" %}{% endwith %} {% with field=form.username type='text' %}{% include "field.html" %}{% endwith %} {% with field=form.password type='password' %}{% include "field.html" %}{% endwith %} {% with field=form.email type='text' %}{% include "field.html" %}{% endwith %} {% with field=form.first_name type='text' %}{% include "field.html" %}{% endwith %} {% with field=form.last_name type='text' %}{% include "field.html" %}{% endwith %} </fieldset> <div class="button-group"> <input type="submit" value="Add" /> </div> </form> </div> </div> テンプレート2つで、フィールドをカスタマイズできる
  • 21.
  • 22.
    Django Multi-DB • 1.2で導入された •バックエンドDBのマスタ スレーブ化、シャーディン グ、レプリケーションを可 能にする • モデル単位でDBのルーティ ングができる Django App Replicate/Partition RO Access RW Access
  • 23.
    Multi-DBの構成 • settings に静的に定義 •名前で区別する • DBのパラメタは辞書 • using() またはデータベー スルータで制御する BE_PG = 'django.db.backends.postgresql_psycopg2' BE_MY = 'django.db.backends.mysql' DATABASES = { 'default': { 'NAME': 'master_db', 'ENGINE': BE_PG, 'USER': 'master_user', 'PASSWORD': 'boo hoo woo' }, 'replicon': { 'NAME': 'replicon_db', 'ENGINE': BE_MY, 'USER': 'replicon_user', 'PASSWORD': 'let it go' } } # 名前でデータベースを指定する User.objects.using('users').get(...) ! # データベースルータを使う class MyDbRouter(object): def db_for_read(self, model, **kw): if ...: return 'replicon' DATABASE_ROUTERS = ['foo.bar.MyDbRouter',...]
  • 24.
    Multi-DBの実装 • データベース名は文字列 • django.db.connections を使ってDB接続を取り出 している •connections は辞書ライ クなインタフェースを持つ ConnectionHandlerイン スタンス from django.db import connections ! class QuerySet(object): """ Represents a lazy database lookup for a set of objects. """ def __init__(self, model=None, query=None, using=None): self.model = model self._db = using self.query = query or sql.Query(self.model) ... ! def ... connection = connections[self.db] django.db.models.query from django.db.utils import ConnectionHandler connections = ConnectionHandler() django.db.__init__
  • 25.
    Multi-DBの実装 • ConnectionHandlerはDB 設定とコネクションプール をキャッシュしているらし い • __getitem__で何かゴニョ ゴニョして取り出している 模様 class ConnectionHandler(object): def __init__(self, databases=None): self._databases = databases self._connections = local() ! ... ! def __getitem__(self, alias): if hasattr(self._connections, alias): return getattr(self._connections, alias) ! self.ensure_defaults(alias) db = self.databases[alias] backend = load_backend(db['ENGINE']) conn = backend.DatabaseWrapper(db, alias) setattr(self._connections, alias, conn) return conn django.db.utils
  • 26.
  • 27.
  • 28.