How do you get at a RadioFieldRenderer object from a template? (newforms)

450 views
Skip to first unread message

Joseph Heck

unread,
May 6, 2007, 2:53:17 PM5/6/07
to django...@googlegroups.com
I'm missing the connection in newforms to get to the pieces I want to
manipulate in the templates.

I have a newform with a single ChoiceField, set up with a RadioSelect() widget:

example_choices = ((1,'a'),
(2,'b'),
(3,'c'),)

class ExampleForm(forms.Form):
choices = forms.ChoiceField(choices=example_choices,widget=forms.RadioSelect())

def exampleview(request):
form = ExampleForm()
return render_to_response('example.html', {'form': form})

When I get that into the template, I can get to the choices:
{{form.choices}}
which renders out the <ul><li...</ul> bits.

In the newforms tests
(http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py#L643),
there's an example where it appears that you can get to the individual
pieces of that list set, and I'd like to be able to pull those apart
in the template and render that up a little differently from the
default.

As far as I understand it, {{form.choices}} is handing back a
BoundField object to the template context to work with - but I don't
know how I can delve through that to get to the RadioFieldRenderer,
where I can pull apart the choice list to more detail.

I'm hoping that I can write out something like this in a template to
style it up a little differently:

<ul class="myclass">
{% for rb in form.choices.something %}
{{ rb }}
{% endfor %}

Or even break it down farther and use rb.name, rb.value,
rb.choice_value, and rb.choice_label to display up the radio button
selection pieces. "rb" in this case being the RadioInput objects that
are shown in the tests at
http://code.djangoproject.com/browser/django/trunk/tests/regressiontests/forms/tests.py#L655

Nick

unread,
May 6, 2007, 8:58:00 PM5/6/07
to Django users
I needed to do a similar thing... in my case I needed the radio inputs
in separate table cells.

The way I did it was to write a custom widget, but I'd also be
interested to hear of another way to achieve the same thing.

Cheers,
Nick

On May 7, 3:53 am, "Joseph Heck" <joseph.h...@gmail.com> wrote:
> I'm missing the connection in newforms to get to the pieces I want to
> manipulate in the templates.
>
> I have a newform with a single ChoiceField, set up with a RadioSelect() widget:
>
> example_choices = ((1,'a'),
> (2,'b'),
> (3,'c'),)
>
> class ExampleForm(forms.Form):
> choices = forms.ChoiceField(choices=example_choices,widget=forms.RadioSelect())
>
> def exampleview(request):
> form = ExampleForm()
> return render_to_response('example.html', {'form': form})
>
> When I get that into the template, I can get to the choices:
> {{form.choices}}
> which renders out the <ul><li...</ul> bits.
>
> In the newforms tests

> (http://code.djangoproject.com/browser/django/trunk/tests/regressionte...),


> there's an example where it appears that you can get to the individual
> pieces of that list set, and I'd like to be able to pull those apart
> in the template and render that up a little differently from the
> default.
>
> As far as I understand it, {{form.choices}} is handing back a
> BoundField object to the template context to work with - but I don't
> know how I can delve through that to get to the RadioFieldRenderer,
> where I can pull apart the choice list to more detail.
>
> I'm hoping that I can write out something like this in a template to
> style it up a little differently:
>
> <ul class="myclass">
> {% for rb in form.choices.something %}
> {{ rb }}
> {% endfor %}
>
> Or even break it down farther and use rb.name, rb.value,
> rb.choice_value, and rb.choice_label to display up the radio button
> selection pieces. "rb" in this case being the RadioInput objects that

> are shown in the tests athttp://code.djangoproject.com/browser/django/trunk/tests/regressionte...

Malcolm Tredinnick

unread,
May 7, 2007, 3:15:51 AM5/7/07
to django...@googlegroups.com
Hey Joe,

You can't get to this information directly from templates using this
widget, because the function calls you need to make to cause the
rendering to happen all require arguments. Part of the implicit design
philosophy in template variable access is that although you can call
methods on the instance, they should be methods that act like attributes
or properties: be able to work out the result completely from a
knowledge of "self", without requiring external arguments.

One solution to your problem requires a change to core. It looks like a
change that isn't harmful and might be useful, though, so it's worth
considering. We could change BoundField.as_widget() so that the "widget"
parameter had a default of None and, in that case, the code used
"self.field.widget" as the value for "widget".

Then you could get access to the RadioInput objects by iterating over
form.choices.as_widget in your example. This works because the
as_widget() method returns the result of RadioSelect.render(), which is
a RadioFieldRenderer instance, which is an iterator over RadioInput
classes.

The argument against making this change is that as far as I can work
out, it's really only useful for the RadioSelect case, which implies we
might be making a fix in the wrong place and should be looking at
RadioSelect instead. I'm wondering if making RadioFieldRenderer an
attribute of the RadioSelect class is another solution. Then you could
sub-class RadioFieldRenderer to return whatever you want (you only have
to override __unicode__) and just use it normally. The argument against
this "argument against" is that then the HTML fragment specifying how a
line is to be formatted is out of control of the template designers and
back into programming country, which is why I suggested the first
solution first. I'm still tossing the ideas over in my mind, but would
be interested to hear other thoughts.

As far as I can tell, there's no clean way to get the access you want
without hacking on core. BoundField is the thing producing the string
you want to poke around inside of and that is intimately tied to Form
(sensibly so, since there's no real need to be able to massively
customise it).

Regards,
Malcolm

Bill Fenner

unread,
May 7, 2007, 7:53:41 PM5/7/07
to django...@googlegroups.com
I did this with an accessor function in the form class:

class NonWgStep1(forms.Form):
add_edit = forms.ChoiceField(choices=(
('add', 'Add a new entry'),
('edit', 'Modify an existing entry'),
('delete', 'Delete an existing entry'),
), widget=forms.RadioSelect)
def add_edit_fields(self):
field = self['add_edit']
return field.as_widget(field.field.widget)

Then in the template, I can use form.add_edit_fields.0, etc.

I am using this in the (probably not uncommon) case of having other
form elements that go with each radio button (e.g., "modify" has a
ChoiceField of things to modify next to it).

Bill

Joseph Heck

unread,
May 7, 2007, 7:59:02 PM5/7/07
to django...@googlegroups.com
Interesting solution Bill! Thanks for sharing! I'll see if I can't
make that mechanism work for me...

Malcolm's response was also great in detail, and now I think I need to
digest some and see how I can use it, and if there's a good generic
solution for me.

-joe

Reply all
Reply to author
Forward
0 new messages