在Django表单中,如何呈现单选按钮以便在页面上分离选项?

时间:2022-11-23 17:18:43

I have a Django form with a two-choice radio button element. I want the form to render more or less like this:

我有一个带有双选单选按钮元素的Django表单。我希望表单或多或少像这样渲染:

( ) I prefer beer
     The last sporting event I attended was: [           ]
     My favorite NASCAR driver is:           [           ]

( ) I prefer wine
     The last opera/play I attended was:     [           ]
     My favorite author is:                  [           ]

In other words, I want to split up the two radio button choices. How do I do that? Using the default form.as_table rendering, the choices are drawn right next to each other which I don't want.

换句话说,我想分开两个单选按钮选项。我怎么做?使用默认的form.as_table渲染,选项彼此相邻,我不想要。

(Apologies to NASCAR and opera enthusiasts.)

(向NASCAR和歌剧爱好者道歉。)

2 个解决方案

#1


5  

I did a little digging, and found a lead in the answer to this similar post that points to a somewhat outdated post in django-users.

我做了一点挖掘,并找到了这个类似帖子的答案的主角,指向django-users中有点过时的帖子。

Borrowing some code from the as_widget() method in forms.py I fashioned a method that I could add to my form in order to retrieve the RadioSelect widget's choices rendered as HTML.

从forms.py中的as_widget()方法借用一些代码我制作了一个方法,我可以将其添加到我的表单中,以便检索以HTML格式呈现的RadioSelect小部件选项。

class MyForm(forms.Form):
    MY_CHOICES = (
        ('opt0', 'Option zero'),
        ('opt1', 'Option one'),
    )
    myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=MY_CHOICES)

    def myfield_choices(self):
        """
        Returns myfield's widget's default renderer, which can be used to 
            render the choices of a RadioSelect widget.
        """
        field = self['myfield']
        widget = field.field.widget

        attrs = {}
        auto_id = field.auto_id
        if auto_id and 'id' not in widget.attrs:
            attrs['id'] = auto_id

        name = field.html_name

        return widget.get_renderer(name, field.value(), attrs=attrs)

Then in the template you can access individual radio button choices like so:

然后在模板中,您可以访问单个单选按钮选项,如下所示:

<ul>
    <li>
        {{ myform.myfield_choices.0 }}
        My custom HTML that goes with choice 0 
    </li>
    <li>
        {{ myform.myfield_choices.1 }}
        Different HTML that goes with choice 1 
    </li>
</ul>

or

要么

{% for choice in myform.myfield_choices %}
    <div>
        {{ choice }}
    </div>
{% endfor %}

I'm pretty sure this is a bad idea. It will likely stop working at some point as django evolves. It violates DRY by copying code from as_widget(). (TBH, I didn't take the time to fully understand the code from as_widget) I'm using it as a temporary hack only. Perhaps there is a better way that involves custom template tags. If so, please let me know.

我很确定这是一个坏主意。随着django的发展,它可能会在某些时候停止工作。它通过从as_widget()复制代码来违反DRY。 (TBH,我没有花时间完全理解as_widget中的代码)我只是将它用作临时黑客。也许有一种更好的方法涉及自定义模板标签。如果是这样,请告诉我。

#2


4  

The RadioSelect widget will render its output by default into an unordered list.

RadioSelect小部件默认将其输出呈现为无序列表。

#forms.py
BEER = 0
WINE = 1
PREFERRED_DRINK_CHOICES = (
    (BEER, 'Beer'),
    (WINE, 'Wine'),
)

class DrinkForm(forms.Form):
    preferred_drink = forms.ChoiceField(choices=PREFERRED_DRINK_CHOICES,
                                        widget=forms.RadioSelect())

#views.py
def test(request):
    form = DrinkForm(request.POST or None)
    if request.method == 'POST':
        if form.is_valid():
            print 'form was valid'
    return render_to_response('test.html', {'form' : form},
        context_instance=RequestContext(request))

#test.html
<form action="." method="post" enctype="multipart/form-data">
    <ul>
        <li>
            {{ form.name.label_tag }}
            {{ form.name }}
            {{ form.name.errors }}
        </li>
        <li>
            {{ form.email.label_tag }}
            {{ form.email }}
            {{ form.email.errors }}
        </li>
        <li>
            {{ form.preferred_drink.label_tag }}
            {{ form.preferred_drink }}
            {{ form.preferred_drink.errors }}
        </li>
        <li>
        <input type="submit" value="Submit" />
        </li>
    </ul>
</form>

Will output:

将输出:

<form enctype="multipart/form-data" method="post" action=".">
    <ul>
    <li>
            <label for="id_name">Name</label>
            <input type="text" maxlength="50" name="name" id="id_name">
    </li>
    <li>
        <label for="id_email">Email</label>
        <input type="text" id="id_email" name="email">
    </li>
    <li>
        <label for="id_preferred_drink_0">Preferred drink</label>
        <ul>
                    <li>
                        <label for="id_preferred_drink_0">
                            <input type="radio" name="preferred_drink" value="0" id="id_preferred_drink_0"> Beer</label>
                    </li>
                    <li>
                        <label for="id_preferred_drink_1"><input type="radio" name="preferred_drink" value="1" id="id_preferred_drink_1"> Wine</label>
                    </li>
                </ul>
        </li>
    <li>
        <input type="submit" value="Submit">
    </li>
    </ul>
</form>

That will render each radio choice on its own line. You'll probably need to add some CSS to get the rendering just the way you want, but that should get you the structure you need. Of course, you can shortcut writing the HTML by hand an just do...

这将使每个无线电选择在自己的线上。你可能需要添加一些CSS来获得你想要的渲染,但这应该会得到你需要的结构。当然,你可以手工编写HTML快捷方式...

<form action="." method="post">
    <ul>
        {{ form.as_ul }}
        <li>
            <input type="submit" value="Submit" />
        </li>
    </ul>
</form>

...but I prefer to write my HTML by hand.

...但我更喜欢手工编写HTML。

#1


5  

I did a little digging, and found a lead in the answer to this similar post that points to a somewhat outdated post in django-users.

我做了一点挖掘,并找到了这个类似帖子的答案的主角,指向django-users中有点过时的帖子。

Borrowing some code from the as_widget() method in forms.py I fashioned a method that I could add to my form in order to retrieve the RadioSelect widget's choices rendered as HTML.

从forms.py中的as_widget()方法借用一些代码我制作了一个方法,我可以将其添加到我的表单中,以便检索以HTML格式呈现的RadioSelect小部件选项。

class MyForm(forms.Form):
    MY_CHOICES = (
        ('opt0', 'Option zero'),
        ('opt1', 'Option one'),
    )
    myfield = forms.ChoiceField(widget=forms.RadioSelect, choices=MY_CHOICES)

    def myfield_choices(self):
        """
        Returns myfield's widget's default renderer, which can be used to 
            render the choices of a RadioSelect widget.
        """
        field = self['myfield']
        widget = field.field.widget

        attrs = {}
        auto_id = field.auto_id
        if auto_id and 'id' not in widget.attrs:
            attrs['id'] = auto_id

        name = field.html_name

        return widget.get_renderer(name, field.value(), attrs=attrs)

Then in the template you can access individual radio button choices like so:

然后在模板中,您可以访问单个单选按钮选项,如下所示:

<ul>
    <li>
        {{ myform.myfield_choices.0 }}
        My custom HTML that goes with choice 0 
    </li>
    <li>
        {{ myform.myfield_choices.1 }}
        Different HTML that goes with choice 1 
    </li>
</ul>

or

要么

{% for choice in myform.myfield_choices %}
    <div>
        {{ choice }}
    </div>
{% endfor %}

I'm pretty sure this is a bad idea. It will likely stop working at some point as django evolves. It violates DRY by copying code from as_widget(). (TBH, I didn't take the time to fully understand the code from as_widget) I'm using it as a temporary hack only. Perhaps there is a better way that involves custom template tags. If so, please let me know.

我很确定这是一个坏主意。随着django的发展,它可能会在某些时候停止工作。它通过从as_widget()复制代码来违反DRY。 (TBH,我没有花时间完全理解as_widget中的代码)我只是将它用作临时黑客。也许有一种更好的方法涉及自定义模板标签。如果是这样,请告诉我。

#2


4  

The RadioSelect widget will render its output by default into an unordered list.

RadioSelect小部件默认将其输出呈现为无序列表。

#forms.py
BEER = 0
WINE = 1
PREFERRED_DRINK_CHOICES = (
    (BEER, 'Beer'),
    (WINE, 'Wine'),
)

class DrinkForm(forms.Form):
    preferred_drink = forms.ChoiceField(choices=PREFERRED_DRINK_CHOICES,
                                        widget=forms.RadioSelect())

#views.py
def test(request):
    form = DrinkForm(request.POST or None)
    if request.method == 'POST':
        if form.is_valid():
            print 'form was valid'
    return render_to_response('test.html', {'form' : form},
        context_instance=RequestContext(request))

#test.html
<form action="." method="post" enctype="multipart/form-data">
    <ul>
        <li>
            {{ form.name.label_tag }}
            {{ form.name }}
            {{ form.name.errors }}
        </li>
        <li>
            {{ form.email.label_tag }}
            {{ form.email }}
            {{ form.email.errors }}
        </li>
        <li>
            {{ form.preferred_drink.label_tag }}
            {{ form.preferred_drink }}
            {{ form.preferred_drink.errors }}
        </li>
        <li>
        <input type="submit" value="Submit" />
        </li>
    </ul>
</form>

Will output:

将输出:

<form enctype="multipart/form-data" method="post" action=".">
    <ul>
    <li>
            <label for="id_name">Name</label>
            <input type="text" maxlength="50" name="name" id="id_name">
    </li>
    <li>
        <label for="id_email">Email</label>
        <input type="text" id="id_email" name="email">
    </li>
    <li>
        <label for="id_preferred_drink_0">Preferred drink</label>
        <ul>
                    <li>
                        <label for="id_preferred_drink_0">
                            <input type="radio" name="preferred_drink" value="0" id="id_preferred_drink_0"> Beer</label>
                    </li>
                    <li>
                        <label for="id_preferred_drink_1"><input type="radio" name="preferred_drink" value="1" id="id_preferred_drink_1"> Wine</label>
                    </li>
                </ul>
        </li>
    <li>
        <input type="submit" value="Submit">
    </li>
    </ul>
</form>

That will render each radio choice on its own line. You'll probably need to add some CSS to get the rendering just the way you want, but that should get you the structure you need. Of course, you can shortcut writing the HTML by hand an just do...

这将使每个无线电选择在自己的线上。你可能需要添加一些CSS来获得你想要的渲染,但这应该会得到你需要的结构。当然,你可以手工编写HTML快捷方式...

<form action="." method="post">
    <ul>
        {{ form.as_ul }}
        <li>
            <input type="submit" value="Submit" />
        </li>
    </ul>
</form>

...but I prefer to write my HTML by hand.

...但我更喜欢手工编写HTML。