如何在模板中渲染Django表单字段

12 投票
2 回答
30927 浏览
提问于 2025-04-17 06:27

我想做一个页面,上面有用户列表和复选框,用来标记哪些用户被选中了,这样就可以对选中的用户进行一些操作。我创建了一个表单类,内容如下:

#in forms.py
class UserSelectionForm(forms.Form):
    """form for selecting users"""
    def __init__(self, userlist, *args, **kwargs):
        self.custom_fields = userlist
        super(forms.Form, self).__init__(*args, **kwargs)
        for f in userlist:
            self.fields[str(f.id)] = forms.BooleanField(initial=False)    

    def get_selected(self):
        """returns selected users"""
        return filter(lambda u: self.fields[str(u.id)], self.custom_fields)

在我的模板中,我把用户放在一个表格里,想让这个表格的最后一列是那些复选框。我需要根据复选框的名字一个一个地渲染这些字段。我尝试创建一个模板标签,返回需要的表单元素的HTML代码:

#in templatetags/user_list_tags.py
from django import template
register = template.Library()

#this is django template tag for user selection form
@register.filter 
def user_select_field(form, userid):
    """
    returns UserSelectionForm field for a user with userid
    """
    key = std(userid)
    if key not in form.fields.keys():
        print 'Key %s not found in dict' % key
        return None
        return form.fields[key].widget.render(form, key)

最后,这是我的模板代码:

<form action="" method="post">
{% csrf_token %}
<table class="listtable">
    <tr>
    <th>Username</th>
    <th>Select</th>
    </tr>
{% for u in userlist %}
    <tr>
    <td>{{u.username}}</td>
    <td>{{select_form|user_select_field:u.id}}</td>
    </tr>
{% endfor %}
</table>
<p><input type="submit" value="make actions" /></p>

不过,这样做并没有把这些控件绑定到表单上,因此在提交表单后,验证就失败了。错误信息显示所有自定义字段都是必填的。所以我有几个问题:

  1. 渲染独立的表单字段的正确方法是什么?

  2. 创建这样一个带复选框的表单的正确方法是什么?(我的方法可能不太好,可能有更简单的方法可以实现我想要的效果。)

2 个回答

13

你把模板搞得太复杂了。创建表单时,在每个字段旁边加一个标签,这个操作在表单的 __init__ 方法里进行。

for f in userlist:
    self.fields[str(f.id)] = forms.BooleanField(label=f.username, initial=False)

然后只需要遍历表单中的字段就行了,不用再担心 userlist 了。

{% for field in form %}
<tr>
    <td>{{ field.label_tag }}</td>
    <td>{{ field }}</td>
</tr>
{% endfor %}
8

好的,我想我找到了正确渲染不同表单字段的方法。我是在查看Django的源代码时发现的。Django.forms.forms.BaseForm这个类里面有一个叫_html_output的方法,它会创建一个Django.forms.forms.BoundField的实例,然后把unicode(boundField)添加到HTML输出中。我照着这个做了一遍,结果效果很好:

#in templatetags/user_list_tags.py
from django import template
from django import forms
register = template.Library()

#this is djangp template tag for user selection form
@register.filter
def user_select_field(form, userid):
    """
    returns UserSelectionForm field for a user with userid
    """
    key = str(userid)
    if key not in form.fields.keys():
        print 'Key %s not found in dict' % key
        return None
    #here i use BoundField:
    boundField = forms.forms.BoundField(form, form.fields[key], key)
    return unicode(boundField)

这样生成的HTML和{{form.as_p}}是一样的,所以POST请求看起来也完全一样,表单也能正确处理。

我还修正了一些我表单类里的错误:

#in UserSelectionForm definition:
...
#__init__
for f in userlist:
    self.fields[str(f.id)] = forms.BooleanField(initial=False, required=False) 
#get_selected    
return filter(lambda u: self.cleaned_data[str(u.id)],
    self.custom_fields)

现在看起来一切都按照我预期的工作,没有使用任何JavaScript。

撰写回答