We shall briefly how to work with multi-variable input in Django.
There are four float input variables: \( A \), \( b \), \( w \), and \( T \).
A function compute in the file compute.py makes
a plot of the function \( u(t)=Ae^{-bt}\sin (wt) \)
depending on these four parameters and returns
the name of the plot file. Our task is to define four input fields,
execute the compute function and show the input fields together with
the resulting plot,
cf. Figures 4
and 5.
Figure 4: The input page.

Figure 5: The result page.

Any Django app needs a project, but here we reuse the project
we set up for the scientific hello world examples. We go to
the directory apps/django_apps and create the Django app vib1:
Terminal> python ../../django_project/manage.py startapp vib1
Then we
relative2absolute_path('../../apps/django_apps/vib1/templates'),
   to the TEMPLATE_DIRS tuple in settings.py,vib1 to the INSTALLED_APPS tuple, andurl(r'^vib1/', 'django_apps.vib1.views.index') to the patterns
   call in urls.py.vib1 to reach the application.
The computations in our application are put in a file compute.py
from numpy import exp, cos, linspace
import matplotlib.pyplot as plt
import os, time, glob
def damped_vibrations(t, A, b, w):
    return A*exp(-b*t)*cos(w*t)
def compute(A, b, w, T, resolution=500):
    """Return filename of plot of the damped_vibration function."""
    print os.getcwd()
    t = linspace(0, T, resolution+1)
    y = damped_vibrations(t, A, b, w)
    plt.figure()  # needed to avoid adding curves in plot
    plt.plot(t, y)
    plt.title('A=%g, b=%g, w=%g' % (A, b, w))
    if not os.path.isdir('static'):
        os.mkdir('static')
    else:
        # Remove old plot files
        for filename in glob.glob(os.path.join('static', '*.png')):
            os.remove(filename)
    # Use time since Jan 1, 1970 in filename in order make
    # a unique filename that the browser has not chached
    plotfile = os.path.join('static', str(time.time()) + '.png')
    plt.savefig(plotfile)
    return plotfile
if __name__ == '__main__':
    print compute(1, 0.1, 1, 20)
We can now write models.py and the Input class that defines
the form fields for the four input variables:
from django.db import models
from django.forms import ModelForm
from math import pi
class Input(models.Model):
    A = models.FloatField(
        verbose_name=' amplitude (m)', default=1.0)
    b = models.FloatField(
        verbose_name=' damping coefficient (kg/s)', default=0.0)
    w = models.FloatField(
        verbose_name=' frequency (1/s)', default=2*pi)
    T = models.FloatField(
        verbose_name=' time interval (s)', default=18)
class InputForm(ModelForm):
    class Meta:
        model = Input
Note here that we can provide a more explanatory name than just
the variable name, e.g., ' amplitude (m)' for A. However,
Django will always capitalize these descriptions, so if one really
needs lower case names (e.g., to be compatible with a mathematical notation
or when just listing the unit),
one must start the text with a space, as we have demonstrated above.
We also provide a default
value such that all fields have a value when the user sees
the page.
The views.py file looks as follows:
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.http import HttpResponse
from models import InputForm
from compute import compute
import os
def index(request):
    os.chdir(os.path.dirname(__file__))
    result = None
    if request.method == 'POST':
        form = InputForm(request.POST)
        if form.is_valid():
            form2 = form.save(commit=False)
            result = compute(form2.A, form2.b, form2.w, form2.T)
            result = result.replace('static/', '')
    else:
        form = InputForm()
    return render_to_response('vib1.html',
            {'form': form,
             'result': result,
             }, context_instance=RequestContext(request))
Some remarks are necessary:
os.chdir to the current working directory is necessary
   as Django may be left back in another working directory if you have
   tested other apps.form2 object from form.save is the object we extract
   data from and send to compute, but the original form
   object is needed when making the HTML page through the template.static. The specifications of the
   URL applies tools to find this static directory and then
   the static prefix in the result filename must be removed.
<form method=post action="">{% csrf_token %}
<table>
  {% for field in form %}
    <tr>
    <td>{{ field.name }}</td>
    <td>{{ field }}</td>
    <td>{{ field.label }}</td>
    <td>{{ field.errors }}</td>
    <td></td>
    </tr>
  {% endfor %}
</table>
<p><input type=submit value=Compute></form></p>
<p>
{% if result != None %}
{% load static %}
<img src="{% get_static_prefix %}{{ result }}" width=500>
{% endif %}
</p>
The tricky part is the syntax for displaying static content, such as
the plot file made in the compute function.
Django has a series of methods available for user-provided validation
of form data. These are exemplified in the app vib2, which
is an extension of the vib1 app with additional code. (This other
app needs of course registrations in settings.py and urls.py, similar
to what we did for the vib1 app.)
Making sure that \( A>0 \) is easiest done with a built-in Django validator for minimum value checking:
class Input(models.Model):
    A = models.FloatField(
        verbose_name=' amplitude (m)', default=1.0,
        validators=[MinValueValidator(0)])
We can write our own validators, which are functions taking the value
is the only argument and raising a ValidationError exception if the
value is wrong. Checking that a value is inside an interval can first
be implemented by
def check_interval(value, min_value=None, max_value=None):
    """Validate that a value is inside an interval."""
    failure = False
    if min_value is not None:
        if value < min_value:
            failure = True
    if max_value is not None:
        if value > max_value:
            failure = True
    if failure:
        raise ValidationError(
            'value=%s not in [%s, %s]' %
            (value,
             '-infty' if min_value is None else str(min_value),
             'infty' if max_value is None else str(max_value)))
However, this function takes more than the value as argument. We therefore
need to wrap it by a function with value as the only argument. The following
utility returns such a
function:
import functools
def interval(min_value=None, max_value=None):
    """Django-compatible interface to check_interval."""
    return functools.partial(
        check_interval, min_value=min_value, max_value=max_value)
Now, interval(0, 1) returns a function that takes value as its
only argument and checks if it is inside \( [0,1] \).
Such a function can be inserted in the validators list in
the field constructor, here to tell that \( b \) must be in \( [0,\infty) \):
class Input(models.Model):
    ...
    b = models.FloatField(
        verbose_name=' damping coefficient (kg/s)', default=0.0,
        validators=[interval(0,None)])
A final example on custom validation is to avoid plotting more
than 30 periods of the oscillating function \( u \). This translates
to checking that \( T \) is geater
than 30 periods, i.e., \( T>30\cdot 2\pi/w \). The task is done in
the InputForm class, where any method clean_name can do
validation and adjustment of the field name name. The code for
a clean_T method goes as follows:
class InputForm(ModelForm):
    class Meta:
        model = Input
    def clean_T(self):
        T = self.cleaned_data['T']
        w = self.cleaned_data['w']
        period = 2*pi/w
        if T > 30*period:
            num_periods = int(round(T/period))
            raise ValidationError(
                'Cannot plot as much as %d periods! T < %.2f' %
                (num_periods, 30*period))
        return T
We refer to the vast Django documentation for many other ways of
validating forms. The reader is encouraged to run the vib2
application and test out the validations we have implemented.
One will occasionally have full control of the layout of the individual
elements in a web form. These are typeset inside input tags in
HTML. Django associates the term widget with an HTML form field. To set
the size (width) of the field and other properties, one must in
Django specify a widgets dictionary in the form class. The key
is the name of the parameter in the model class, while the value
is a widget class name. Standard input fields for numbers and
text apply the TextInput widget. An example
on setting the size of the T field to a width of 10 characters goes
like
from django.forms import TextInput
class InputForm(ModelForm):
    class Meta:
        model = Input
        widgets = {
            'T': TextInput(attrs={'size': 10}),
        }
At the time of this writing, Django does not yet support the many
additional HTML5 input fields. Nevertheless, the parampool package
gives access to HTML5 widgets in a Django context.
We recommend to use parampool to automatically generate the necessary
Django files, and
then one can view the form class in the models.py file for how
HTML5 widgets can be used in the definition of the widgets
dictionary.