django.forms.widget-ChoiceFieldRenderer

本文深入解析ChoiceFieldRenderer在HTML表单中如何实现radio和checkbox的定制展示,包括其内部逻辑和输出的HTML样式,以及RadioFieldRenderer和CheckboxFieldRenderer的用法。同时介绍了RendererMixin类作为RadioSelect和CheckboxSelectMultiple的基类,如何通过指定renderer属性来输出不同的HTML结构。


发表于10个月前(2014-12-18 09:55)   阅读( 5) | 评论( 00人收藏此文章,我要收藏
赞0


摘要 ChoiceFieldRenderer负责html表单中多个radio和check的展示。比如生成ul和li包含的语句

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class  ChoiceFieldRenderer( object ):
     """
     An object used by RadioSelect to enable customization of radio widgets.
     """
 
     choice_input_class  =  None
     outer_html  =  '<ul{id_attr}>{content}</ul>'
     inner_html  =  '<li>{choice_value}{sub_widgets}</li>'
 
     def  __init__( self , name, value, attrs, choices):
         self .name  =  name
         self .value  =  value
         self .attrs  =  attrs
         self .choices  =  choices
 
     def  __getitem__( self , idx):
         choice  =  self .choices[idx]   # Let the IndexError propagate
         return  self .choice_input_class( self .name,  self .value,  self .attrs.copy(), choice, idx)
 
     def  __str__( self ):
         return  self .render()
 
     def  render( self ):
         """
         Outputs a <ul> for this set of choice fields.
         If an id was given to the field, it is applied to the <ul> (each
         item in the list will get an id of `$id_$i`).
         """
         id_  =  self .attrs.get( 'id' None )
         output  =  []
         for  i, choice  in  enumerate ( self .choices):
             choice_value, choice_label  =  choice
             if  isinstance (choice_label, ( tuple list )):
                 attrs_plus  =  self .attrs.copy()
                 if  id_:
                     attrs_plus[ 'id' + =  '_{0}' . format (i)
                 sub_ul_renderer  =  ChoiceFieldRenderer(name = self .name,
                                                       value = self .value,
                                                       attrs = attrs_plus,
                                                       choices = choice_label)
                 sub_ul_renderer.choice_input_class  =  self .choice_input_class
                 output.append(format_html( self .inner_html, choice_value = choice_value,
                                           sub_widgets = sub_ul_renderer.render()))
             else :
                 =  self .choice_input_class( self .name,  self .value,
                                             self .attrs.copy(), choice, i)
                 output.append(format_html( self .inner_html,
                                           choice_value = force_text(w), sub_widgets = ''))
         return  format_html( self .outer_html,
                            id_attr = format_html( ' id="{0}"' , id_)  if  id_  else  '',
                            content = mark_safe( '\n' .join(output)))


ChoiceFieldRenderer输出的html的格式为:

?
1
2
3
4
< ul >
     < li >< label  for = " " > < input  /> label_text </ li >
     ...
</ ul >

这里面所有的标签都共用 name,value,attrs中的属性。

render()函数其实含有递归调用的概念。


RadioFieldRenderer和CheckboxFieldRenderer只是指定了实例化<label ><input/>..</label>的类。

?
1
2
3
4
5
6
class  RadioFieldRenderer(ChoiceFieldRenderer):
     choice_input_class  =  RadioChoiceInput
 
 
class  CheckboxFieldRenderer(ChoiceFieldRenderer):
     choice_input_class  =  CheckboxChoiceInput


举个简单的例子, 以RadioFieldRenderer为例:

?
1
2
3
4
5
6
7
8
9
choices  =  (( 'apple_value' 'apple' ),( 'banana_value' 'banana' ))
radio  =  RadioFieldRenderer( 'fruit' 'banana_value' , { 'id' 'radio_id' }, choices)
print  radio.render()
 
#结果为
#<ul id="radio_id">
#<li><label for="radio_id_0"><input id="radio_id_0" name="fruit" type="radio" value="apple_value" />apple</label></li>
#<li><label for="radio_id_1"><input checked="checked" id="radio_id_1" name="fruit" type="radio" value="banana_value" /> banana</label></li>
#</ul>

通过设置choices的格式,可以<li>里面嵌套<ui>。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
choices  = 
( 'Asia' , ( 
             ( 'apple_china_value' 'apple_china' ), 
             ( 'apple_japan_value' 'japan_china'
            
          ), 
( 'Europe' 'Europe_value' )
)
 
radio  =  RadioFieldRenderer( 'fruit' 'banana_choosen' , { 'id' 'radio_id' }, choices)
print  radio.render()
 
#结果为:
#<ul id="radio_id">
#<li>Asia<ul id="radio_id_0">
#<li><label for="radio_id_0_0"><input id="radio_id_0_0" name="fruit" type="radio" value="apple_china_value" /> apple_china</label></li>
#<li><label for="radio_id_0_1"><input id="radio_id_0_1" name="fruit" type="radio" value="apple_japan_value" /> japan_china</label></li>
#</ul></li>
#<li><label for="radio_id_1"><input id="radio_id_1" name="fruit" type="radio" value="Europe" /> Europe_value</label></li>
#</ul>


下面是RendererMixin类,它是RadioSelect,CheckboxSelectMultiple的基类。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class  RendererMixin( object ):
     renderer  =  None   # subclasses must define this
     _empty_value  =  None
 
     def  __init__( self * args,  * * kwargs):
         # Override the default renderer if we were passed one.
         renderer  =  kwargs.pop( 'renderer' None )
         if  renderer:
             self .renderer  =  renderer
         super (RendererMixin,  self ).__init__( * args,  * * kwargs)
 
     def  subwidgets( self , name, value, attrs = None , choices = ()):
         for  widget  in  self .get_renderer(name, value, attrs, choices):
             yield  widget
 
     def  get_renderer( self , name, value, attrs = None , choices = ()):
         """Returns an instance of the renderer."""
         if  value  is  None :
             value  =  self ._empty_value
         final_attrs  =  self .build_attrs(attrs)
         choices  =  list (chain( self .choices, choices))
         return  self .renderer(name, value, final_attrs, choices)
 
     def  render( self , name, value, attrs = None , choices = ()):
         return  self .get_renderer(name, value, attrs, choices).render()
 
     def  id_for_label( self , id_):
         # Widgets using this RendererMixin are made of a collection of
         # subwidgets, each with their own <label>, and distinct ID.
         # The IDs are made distinct by y "_X" suffix, where X is the zero-based
         # index of the choice field. Thus, the label for the main widget should
         # reference the first subwidget, hence the "_0" suffix.
         if  id_:
             id_  + =  '_0'
         return  id_

整个思路是指定类属性 renderer的值,来确定使用那个render来输出html语句。

类属性_empty_value,仅仅用来当value=None时,默认为空时的值。


通过下面两个类,就很清楚。

?
1
2
3
4
5
6
7
8
class  RadioSelect(RendererMixin, Select):
     renderer  =  RadioFieldRenderer
     _empty_value  =  ''
 
 
class  CheckboxSelectMultiple(RendererMixin, SelectMultiple):
     renderer  =  CheckboxFieldRenderer
     _empty_value  =  []
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值