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

Posted

技术标签:

【中文标题】在 Django 表单中,如何呈现单选按钮以便在页面上分隔选项?【英文标题】:In a Django form, how do I render a radio button so that the choices are separated on the page? 【发布时间】:2011-10-09 16:01:17 【问题描述】:

我有一个带有两个选择单选按钮元素的 Django 表单。我希望表单或多或少像这样呈现:

( ) 我更喜欢啤酒 我参加的最后一场体育赛事是:[] 我最喜欢的 NASCAR 车手是:[ ] ( ) 我更喜欢酒 我参加的最后一部歌剧/戏剧是:[] 我最喜欢的作者是:[]

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

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

【问题讨论】:

【参考方案1】:

我进行了一些挖掘,并在similar post 的答案中找到了一个线索,该线索指向了一个有点过时的post in django-users。

从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)

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

<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>

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

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

【讨论】:

【参考方案2】:

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>

将输出:

<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>

这会将每个单选选项呈现在自己的行上。您可能需要添加一些 CSS 以按照您想要的方式进行渲染,但这应该会为您提供所需的结构。当然,你也可以直接手动编写 HTML 的捷径……

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

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

【讨论】:

但这仍然会在相邻行上打印两个选项,对吧?这不是我想要做的。我想让两个单选按钮分开,每个按钮下面都有块字段,就像我的 nascar/opera 示例一样。 如果您手动编写 HTML,您可以将其他字段或标记放在您想要的任何位置。使用 form.as_ul 会限制你能做什么。 呈现单选单选按钮元素的语法是什么? form.preferred_drink 呈现所有选择。 从模板级别获得特定选择并不容易。您可以像字典一样遍历选项: % for value, text in form.preferred_drink.field.choices % value , text % endfor % 但是,不支持索引模板级别以获得您想要的选择。我建议为视图中的选项设置变量,或者编写一个模板标签来接受您想要的选项的字段和索引,然后呈现输入 html。 只是为了添加一个更新的 Django 功能。您现在可以访问索引。 form.preferred_drink.field.choices.0 将有 ('val', 'label text')。所以 form.preferred_drink.field.choices.0.0 将打印值, form.preferred_drink.field.choices.0.1 将打印标签文本。

以上是关于在 Django 表单中,如何呈现单选按钮以便在页面上分隔选项?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Django 中呈现单个单选按钮选项?

如何编写自己的 Django 表单小部件? [复制]

如何根据在模型级别定义的枚举字段在 Django Modelform 中呈现单选按钮选择?

如何在 django 中创建表单和模型中使用的字段的单选按钮

使用 django 和引导类创建单选表单的问题

Django BooleanField 作为单选按钮?