Django的Form機制詳解
環境:Python 2.7.5 + Django 1.6
使用Django,我們可以以宣告式的方式來定義一個Form,如下:
# -*- coding: utf-8 -*- from django import forms class SimpleForm(forms.Form): field_a = forms.CharField(max_length=100) field_b = forms.CharField(max_length=100)
寫起來很舒服,但是問題來了,當我把這個Form初始化之後,比如:
from polls.forms import SimpleForm
sf = SimpleForm({'field_a':'value of field_a', 'field_b':'value of field_b'})
然後在python shell裡執行dir(sf),發現該例項並沒有field_a和field_b這兩個屬性,顯然我們就不能像sf.field_a這麼來引用sf上的欄位了。可是明明我們可以在template裡以{{ form_name.field_name }}的形式來引用form的欄位,這是怎麼回事呢?
一番調查之後發現背後的實現機制還比較曲折。首先,如果我們要引用form裡的欄位應該怎麼寫呢?應該這麼寫:sf['field_a']
為什麼要這麼寫呢?上程式碼,django.forms.BaseForm的__getitem__方法:
def __getitem__(self, name): "Returns a BoundField with the given name." try: field = self.fields[name] except KeyError: raise KeyError('Key %r not found in Form' % name) return BoundField(self, field, name)
這樣就把BaseForm變成一個像dict一樣的的容器了,所以可以用上面的語法來引用form裡的欄位。
新的問題又來了,為什麼可以在template裡以{{ form_name.field_name }}的形式來引用form的欄位呢?見Django的官方文件:https://docs.djangoproject.com/en/1.6/topics/templates/#variables。原來Django的模板引擎碰到{{ form_name.field_name }}這樣的表示式,會在form_name物件上執行字典查詢,所以模板引擎對{{ sf.field_a }}求值時實際上執行了sf['field_a'],真相大白了。
另外,上文中的SimpleForm的型別實際上是django.forms.DeclarativeFieldsMetaclass。這個元類實際上是把SimpleForm中以宣告式語法宣告的所有欄位(還包括父類中的宣告式欄位)透過get_declared_fields方法轉換成了一個dict,並將dict的值賦給了將要生成的類的base_fields屬性,然後基於SimpleForm生成了一個新的類。