Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to render django form field in template

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.

like image 935
Victor Proon Avatar asked Nov 16 '11 19:11

Victor Proon


People also ask

How can I get form data in Django?

Using Form in a View In Django, the request object passed as parameter to your view has an attribute called "method" where the type of the request is set, and all data passed via POST can be accessed via the request. POST dictionary. The view will display the result of the login form posted through the loggedin.

What is form Is_valid () in Django?

The is_valid() method is used to perform validation for each field of the form, it is defined in Django Form class. It returns True if data is valid and place all data into a cleaned_data attribute.

What is template rendering in Django?

Rendering means interpolating the template with context data and returning the resulting string. The Django template language is Django's own template system. Until Django 1.8 it was the only built-in option available. It's a good template library even though it's fairly opinionated and sports a few idiosyncrasies.


2 Answers

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 %}
like image 114
Chris Pratt Avatar answered Oct 11 '22 23:10

Chris Pratt


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.

like image 33
Victor Proon Avatar answered Oct 11 '22 22:10

Victor Proon