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

被折叠的 条评论
为什么被折叠?



