Create and validate dynamic forms using Django

26 views
Skip to first unread message

Shivul

unread,
Nov 11, 2008, 2:10:51 PM11/11/08
to Django users
Warning: New to Django so bear with me but got some really helpful
stuff
out here. Also have been using Django 0.96 so change accordingly for
version 1.0.(specifically forms.py)

Problem: Create and validate dynamic forms using Django.
1) Creation should be dynamic
2) Validation to be done from server-side.

Additional requirements:
1) JQuery (just for clone function)


Some funda:

Consider the following case where we add a contact using a form.

file : models.py

from django.db import models
class Contacts(models.Model):
name = models.CharField(maxlength=20)
address = models.CharField(maxlength=20)
updated = models.DateTimeField(null=False, auto_now =True)
created = models.DateTimeField(null=False, auto_now_add=True)
class Admin:
pass
#columns that will appear on the list pages
list_display = ('name', 'address')
def __str__(self):
return '%s %s' % (self.name, self.address)

file : views.py
from xxxx.packages form import ContactsForm
def addContacts(request):
manipulator = ContactsForm()
if request.POST:
new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data)
if not errors:
manipulator.do_html2python(new_data)
new_contact = manipulator.save(new_data)
form = forms.FormWrapper(manipulator)
return render_to_response('contact.html', {'form':
form)
else:
errors = new_data = {}
form = forms.FormWrapper(manipulator, new_data, errors)
return render_to_response('contact.html', {'form': form})

file :forms.py
from django import forms
from django.core import validators
from xxxx.packages models import Contacts

class ContactsForm(forms.Manipulator):
def __init__(self):
self.fields = (
forms.TextField(field_name='name', length=10,
maxlength=20, is_required=True,

validator_list=[validators.isAlphaNumeric]),
forms.TextField(field_name='address',
length=10, maxlength=20, is_required=True,

validator_list=[validators.isAlphaNumeric]),
)

def save(self, new_data):
u = Contacts(name=new_data['name'],address=new_data['address'])
u.save()
return u

file contact.html

{% extends "base.html" %}

{% block title %}
Log in | {{ block.super }}
{% endblock %}

{% block content %}
<script type="text/javascript" src="http://localhost:8000/media/js/
jquery.js"></script>
<form class="regform" action="." method="post" accept-
charset="utf-8">
<div id = "1">
<h3><label for="name">Name:</label></h3>
<p>
{{ form.name}}
{% if form.name.errors %}
<span class="error">{{ form.name.errors|join:",
" }}</span>
{% endif %}
</p>
<h3><label for="address">Address:</label></h3>
<p>
{{ form.address}}
{% if form.address.errors %}
<span class="error">{{ form.address.errors|join:",
" }}</span>
{% endif %}
</p>
</div>
<input class="button" type="submit" value="Add Address">
</form>
{% endblock %}

Points to note before we go for dynamic form:
1) When we create a form on the front-end through the view.py using
form.py, the form is passed to the template as variable.
2) Then the form gets created in the front end.
3) What happens next is you enter data into the form and post the
form
with entered data.(Hint -1)
4) What you would have written in views.py on post is to get the
new_data
and validate it.
5) The catch here is that you use the same manipulator. (Hint -2)
6) We then validate the data and if error is caught we save it into
"error" and then create the form using
form = forms.FormWrapper(manipulator, new_data, errors) and send
it
back to the front-end where proper messages are displayed.
7) If everything is ok then save the data. (I would escape this part,
hope this is fine)


Now dynamic form:
So if now we want to solve our problem then we need to do the
following:
1)We need to pass an initial form list to template
2)We need to have an add new form button.
3)We need to do some javascript manipulation to clone the form.
4)Then in the views.py we need to catch the new_data and get the no of
forms.
5)We need to form an list of manipulators, new_data(extract data from
POST request) and error.
6)We need to pass the the form_list again to the page in case of any
error.
7)Save the form in case all the validations passes.(left to you, hints
in stuffs while we validate)

Files that requires changes are views.py and contact.html only.
So changes are as follows:

file : views.py
from xxxx.packages form import ContactsForm
def addContacts(request):
manipulator_list =[]
n=2
if request.POST:#post if
errors_list =[]
data_list = []
k=0
new_data = request.POST.copy()
m=len(new_data.getlist('name')) + 1
for i in range(1, m):#forloop1
print i
manipulator = ContactsForm()
manipulator_list.append(manipulator)
else:#forloop1
print 'loop is over'

for manipulator in manipulator_list:#forloop2
olddata = {'name': new_data.getlist('name')
[k],
'address':
new_data.getlist('address')[k]}
data_list.append(olddata)
errors = manipulator.get_validation_errors(olddata)
errors_list.append(errors)
k = k+1
else: #forloop2
print 'The for loop for populating error_list and
data_list dor PST operation is over'

if not errors:
form_list =[]
j=0
for manipulator in manipulator_list:#forloop3
new_contact = manipulator.save(data_list[j],request.user)
form = forms.FormWrapper(manipulator)
form_list.append(form)
j=j+1
else:#forloop3
return render_to_response('contact.html', {'form_list':
form_list})
else:#post if
errors_list = data_list = []
for i in range(1, n):
print i
manipulator = ContactsForm()
manipulator_list.append(manipulator)
else:
print 'The loop is over'
for i in range(1, n):
print i
errors_list.append({})
data_list.append({})
else:
print 'The for loop for emptying
error_list and data_list is over'

form_list =[]
l=0
for manipulator in manipulator_list:
form = forms.FormWrapper(manipulator, data_list[l],
errors_list[l])
form_list.append(form)
l=l+1
else:
print 'The ERROR for loop is over'
return render_to_response('contact.html',
{'form_list': form_list})

file contact.html

{% extends "base.html" %}

{% block title %}
Log in | {{ block.super }}
{% endblock %}

{% block content %}
<script type="text/javascript" src="http://localhost:8000/media/js/
jquery.js"></script>
<form class="regform" action="." method="post" accept-
charset="utf-8">
{% for form in form_list %}
<div id = "inputrow{{forloop.counter}}">
<h3><label for="name">Name:</label></h3>
<p>
{{ form.name}}
{% if form.name.errors %}
<span class="error">{{ form.name.errors|join:",
" }}</span>
{% endif %}
</p>
<h3><label for="address">Address:</label></h3>
<p>
{{ form.address}}
{% if form.address.errors %}
<span class="error">{{ form.address.errors|join:",
" }}</span>
{% endif %}
</p>
</div>
{% endfor %}
<input class="button" type="submit" value="Add
Address">
</form>
<script>
function addrow()
{
$("#inputrow{{ form_list|
length }}").clone(true).insertAfter("#inputrow{{ form_list|
length }}");
}
</script>
<input class="button" type="submit" value="Add Another
Row" onclick = "addrow();">
{% endblock %}


So here we go.
Note: if you want to delete rows and stuffs like that then you need to
do again some JavaScript manipulation.

Hope this helps.

Reply all
Reply to author
Forward
0 new messages