19 декабря 2008 г.

attrs в виджетах django

В процессе написания мультивиджета, пришлось задуматься, а что такое attrs (атрибуты)? По началу было понятно, что это атрибуты поля формы:
  1. <select name="school_user-0-year_start_year" id="id_school_user-0-year_start">  
  2. </select>  

Инициализация виджета (class Widget)
  1. def __init__(self, attrs=None):  
  2.   
  3.           if attrs is not None:  
  4.   
  5.             self.attrs = attrs.copy()  
  6.   
  7.           else:  
  8.   
  9.                 self.attrs = {}  

Возьмем для примера следующий виджет:
  1. class Select(Widget):  
  2.   
  3.     def __init__(self, attrs=None, choices=()):  
  4.   
  5.         super(Select, self).__init__(attrs)  

В принципе понятно, что происходит. Далее, рассмотрим функцию рендеринга виджета:
  1. def render(self, name, value, attrs=None, choices=()):  
  2.   
  3.     if value is None: value = ''  
  4.   
  5.    final_attrs = self.build_attrs(attrs, name=name)  
  6.   
  7.     output = [u'<select%s>' % flatatt(final_attrs)]  
  8.   
  9.     options = self.render_options(choices, [value])  
  10.   
  11.     if options:  
  12.   
  13.         output.append(options)  
  14.   
  15.     output.append('')  
  16.   
  17.     return mark_safe(u'\n'.join(output))  
  18.   
  19. ct%s>  

Что делает функция build_attrs?
  1. def build_attrs(self, extra_attrs=None, **kwargs):  
  2.   
  3.      "Helper function for building an attribute dictionary."  
  4.   
  5.      attrs = dict(self.attrs, **kwargs)  
  6.   
  7.      if extra_attrs:  
  8.   
  9.          attrs.update(extra_attrs)  
  10.   
  11.      return attrs  

Функция принимает **kwargs (keyworded аргументы, то есть аргументы с ключами), в нашем случае передается ключ name с названием данного виджета. В итоге мы получаем примерно следующее:
  1. <select name="school_user-0-year_start_year">  
  2. </select>  

Но откуда берется id?
Оно приходит в атрибутах до рендеренга виджета, из далеких глубин класса class BoundField(StrAndUnicode), который описан в исходниках как "A Field plus data".
Функция класса as_widget собственно и инициирует вызов рендеринга виджета.
  1. def as_widget(self, widget=None, attrs=None, only_initial=False):  
  2.   
  3.       """ 
  4.  
  5.       Renders the field by rendering the passed widget, adding any HTML 
  6.  
  7.       attributes passed as attrs.  If no widget is specified, then the 
  8.  
  9.       field's default widget will be used. 
  10.  
  11.       """  
  12.   
  13.      if not widget:  
  14.   
  15.           widget = self.field.widget  
  16.   
  17.       attrs = attrs or {}  
  18.   
  19.       auto_id = self.auto_id  
  20.   
  21.       if auto_id and 'id' not in attrs and 'id' not in widget.attrs:  
  22.   
  23.           attrs['id'] = auto_id  
  24.   
  25.       if not self.form.is_bound:  
  26.   
  27.           data = self.form.initial.get(self.name, self.field.initial)  
  28.   
  29.           if callable(data):  
  30.   
  31.               data = data()  
  32.   
  33.       else:  
  34.   
  35.           data = self.data  
  36.   
  37.       if not only_initial:  
  38.   
  39.           name = self.html_name  
  40.   
  41.       else:  
  42.   
  43.           name = self.html_initial_name  
  44.   
  45.       return widget.render(name, data, attrs=attrs)  

Поэтому, добавить свои атрибуты или получить данные из словаря в процессе переопределения рендеринга не сложно.
  1. class MyWidget(forms.Select):  
  2.   
  3.     def render(self, name, value, attrs=None, choices=()):  
  4.   
  5.         id_ = attrs.get('id'None)  
  6.   
  7.         attrs.update({'onchange':'javascriptfunction(%s%s);' % id_,name})  
  8.   
  9.         return super(MyWidget, self).render(name, value, attrs)  

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

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

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

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