19 декабря 2008 г.

attrs в виджетах django

В процессе написания мультивиджета, пришлось задуматься, а что такое attrs (атрибуты)? По началу было понятно, что это атрибуты поля формы:



Инициализация виджета (class Widget)

def __init__(self, attrs=None):
if attrs is not None:
self.attrs = attrs.copy()
else:
self.attrs = {}

Возьмем для примера следующий виджет:

class Select(Widget):
def __init__(self, attrs=None, choices=()):
super(Select, self).__init__(attrs)

В принципе понятно, что происходит. Далее, рассмотрим функцию рендеринга виджета:

def render(self, name, value, attrs=None, choices=()):
if value is None: value = ''
final_attrs = self.build_attrs(attrs, name=name)
output = [u'' % flatatt(final_attrs)]
options = self.render_options(choices, [value])
if options:
output.append(options)
output.append('')
return mark_safe(u'\n'.join(output))

Что делает функция build_attrs?

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

Функция принимает **kwargs (keyworded аргументы, то есть аргументы с ключами), в нашем случае передается ключ name с названием данного виджета. В итоге мы получаем примерно следующее:



Но откуда берется id?
Оно приходит в атрибутах до рендеренга виджета, из далеких глубин класса class BoundField(StrAndUnicode), который описан в исходниках как "A Field plus data".
Функция класса as_widget собственно и инициирует вызов рендеринга виджета.

def as_widget(self, widget=None, attrs=None, only_initial=False):
"""
Renders the field by rendering the passed widget, adding any HTML
attributes passed as attrs. If no widget is specified, then the
field's default widget will be used.
"""
if not widget:
widget = self.field.widget
attrs = attrs or {}
auto_id = self.auto_id
if auto_id and 'id' not in attrs and 'id' not in widget.attrs:
attrs['id'] = auto_id
if not self.form.is_bound:
data = self.form.initial.get(self.name, self.field.initial)
if callable(data):
data = data()
else:
data = self.data
if not only_initial:
name = self.html_name
else:
name = self.html_initial_name
return widget.render(name, data, attrs=attrs)

Поэтому, добавить свои атрибуты или получить данные из словаря в процессе переопределения рендеринга не сложно.

class MyWidget(forms.Select):
def render(self, name, value, attrs=None, choices=()):
id_ = attrs.get('id', None)
attrs.update({'onchange':'javascriptfunction(%s%s);' % id_,name})
return super(MyWidget, self).render(name, value, attrs)

Хотя можно передавать атрибуты и в определении самого виджета, функция self.build_attrs() просто обновит словарь новыми записями, но мне надо было именно переопределить атрибуты в виджете, для создания мультивиджета.
attrs.update() в мультивиджетах меняет атрибуты во всех подвиджетах.

1 комментарий:

  1. Блоггер почемуто br-ки понавставлял, баг какой-то, что-то не нашел как его лечить..

    ОтветитьУдалить