How to render django form field in template


Question

I want to make a page with a list of users and checkboxes that signal if a user is selected, which will apply some action to selected users. I created a form class which looks like this:

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

In my template I have users listed in a table and I want the last column of this table to be those checkboxes. I need to render fields one by one depending on their name. I tried creating a template tag that would return the html code of the needed form element:

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

Finally, here's the template code:

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

However, this does not bind those widgets to the form and thus, after submitting the form, validation fails. The error message says that all the custom fields are required. So here are my questions:

  1. What is the right way to render separate form fields?

  2. What is the right way of creating such a form with checkboxes? (I mean maybe my method is stupid and there is a much easier way of achieving what I want.

1
12
11/17/2011 7:23:05 AM

Accepted Answer

Ok So I think I have found a way to correctly render separate form fields. I found it watching django sources. Django.forms.forms.BaseForm class has _html_output method which creates an instance of Django.forms.forms.BoundField and then adds unicode(boundField) to the html output. I did the exact same thing and it worked perfectly:

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

That generated the same html as {{form.as_p}}, so the POST request will look exactly the same and form will be processed correctly.

I also fixed some mistakes in my form class:

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

That now seems to work as I planned, without any javascript.

8
6/2/2017 6:57:07 PM

You're making the template far too complicated. Add a label to each field when you create it in the form's __init__ method.

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

Then just loop over the fields in the form and don't worry about the userlist anymore.

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

Licensed under: CC-BY-SA with attribution
Not affiliated with: Stack Overflow
Icon