newformのメモ
Djangoで、HTMLのfieldの属性等を管理するクラスFieldのメモ。
HTMLのFieldの属性はwidgetというクラスで管理し、Fieldオブジェクトはwidgetオブジェクトを呼び出してごにょごにょするみたい。
基底クラスであるFieldは、下記のような仕様になっている。
コンストラクタの引数は下記
- required
- Boolean値。デフォルト:Treu。どう使うのかわからない。
- widget
- widgetオブジェクト。たいていはTextInputオブジェクトだが、すきなように使うことができる
- label
- ラベル。そのまま。"pretty" versionってなんぞ・・・?
- initial
- 表示の時に使う名前
- help_text
- そのまま。ヘルプテキスト。
インスタンスメソッドは二つしか使わない。
- clean
- HTMLで入力した値が正しいかどうか判断する時に使われる関数。正しくないとき等は、例外の「ValidationError」 を呼び出せば良い。
- widget_attr
- HTMLのフォームの属性をwidgetに与える関数。返り値に辞書型で返せばそのまま出力する。
class Field(object): widget = TextInput # Default widget to use when rendering this type of Field. hidden_widget = HiddenInput # Default widget to use when rendering this as "hidden". # Tracks each time a Field instance is created. Used to retain order. creation_counter = 0 def __init__(self, required=True, widget=None, label=None, initial=None, help_text=None): # required -- Boolean that specifies whether the field is required. # True by default. # widget -- A Widget class, or instance of a Widget class, that should be # used for this Field when displaying it. Each Field has a default # Widget that it'll use if you don't specify this. In most cases, # the default widget is TextInput. # label -- A verbose name for this field, for use in displaying this field in # a form. By default, Django will use a "pretty" version of the form # field name, if the Field is part of a Form. # initial -- A value to use in this Field's initial display. This value is # *not* used as a fallback if data isn't given. # help_text -- An optional string to use as "help text" for this Field. def clean(self, value): """ Validates the given value and returns its "cleaned" value as an appropriate Python object. Raises ValidationError for any errors. """ def widget_attrs(self, widget): """ Given a Widget instance (*not* a Widget class), returns a dictionary of any HTML attributes that should be added to the Widget, based on this Field. """
CharFieldは下記のように使っている。コンストラクタのmax_length,min_lengthが追加されており、cleanでそれが使われている事にも注目。
class CharField(Field): def __init__(self, max_length=None, min_length=None, *args, **kwargs): self.max_length, self.min_length = max_length, min_length super(CharField, self).__init__(*args, **kwargs) def clean(self, value): "Validates max_length and min_length. Returns a Unicode object." super(CharField, self).clean(value) if value in EMPTY_VALUES: return u'' value = smart_unicode(value) if self.max_length is not None and len(value) > self.max_length: raise ValidationError(gettext(u'Ensure this value has at most %d characters.') % self.max_length) if self.min_length is not None and len(value) < self.min_length: raise ValidationError(gettext(u'Ensure this value has at least %d characters.') % self.min_length) return value def widget_attrs(self, widget): if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)): return {'maxlength': str(self.max_length)}
一方でwidgetの方は下記になっている。
- コンストラクタ
- 引数はattrという物だけ。早い話、HTMLのフォームの属性。
- render
- HTMLに整形したUnicodeの文字列を返すようにしたら良い。つまり、HTMLに出力される際に呼び出される。
- build_attrs
- 属性の辞書を整形する際に呼び出される補助関数
- value_from_datadict
- widgetの名前と辞書型のデータが与えられ、widgetの値を返すようにする為の関数。らしい・・・
- id_for_label
- HTMLのID属性を、このwidgetの名前を使って作成する。
class Widget(object): is_hidden = False # Determines whether this corresponds to an <input type="hidden">. def __init__(self, attrs=None): self.attrs = attrs or {} def render(self, name, value, attrs=None): """ Returns this Widget rendered as HTML, as a Unicode string. The 'value' given is not guaranteed to be valid input, so subclass implementations should program defensively. """ raise NotImplementedError def build_attrs(self, extra_attrs=None, **kwargs): "Helper function for building an attribute dictionary." attrs = dict(self.attrs, **kwargs) if extra_attrs: attrs.update(extra_attrs) return attrs def value_from_datadict(self, data, name): """ Given a dictionary of data and this widget's name, returns the value of this widget. Returns None if it's not provided. """ return data.get(name, None) def id_for_label(self, id_): """ Returns the HTML ID attribute of this Widget for use by a <label>, given the ID of the field. Returns None if no ID is available. This hook is necessary because some widgets have multiple HTML elements and, thus, multiple IDs. In that case, this method should return an ID value that corresponds to the first ID in the widget's tags. """ return id_ id_for_label = classmethod(id_for_label)
CharFieldが使っているInputwidgetは下記のようになっている。ほとんど使い回し出来るかも・・・
class Input(Widget): """ Base class for all <input> widgets (except type='checkbox' and type='radio', which are special). """ input_type = None # Subclasses must define this. def render(self, name, value, attrs=None): if value is None: value = '' final_attrs = self.build_attrs(attrs, type=self.input_type, name=name) if value != '': final_attrs['value'] = smart_unicode(value) # Only add the 'value' attribute if a value is non-empty. return u'<input%s />' % flatatt(final_attrs)