Jonathan Livni
Jonathan Livni

Reputation: 107272

django forms and ajax

I'm trying to create a page with two functional parts:

I'm using django forms and jQuery and I can't quite figure out how the design fits together.
Upon submission of the form one of two things should happen, either the chart (and only the chart) updates or, if there is a form validation error, the form section should update with the appropriate error messages. So actually one Ajax call should update either part.
I'd like only the relevant div to update (either form or chart) instead of the entire page which includes the other div, a logo and other static graphics.

One of the challenges is that if the form sumbission went well, I'd like to return a JSON object which updates the chart, as I'm using a 3rd party charting package, however if the submission failes the returned content should be an HTML response with the updated form.

What would be a correct pattern for the django view for this behavior?
What would be a correct javascript\jQuery pattern for this behavior?
If this approach is not correct, what would be an alternative approach?

Upvotes: 2

Views: 3976

Answers (3)

garmoncheg
garmoncheg

Reputation: 906

There is a nice article about this. It really describes the design of AJAX forms with Django. There are all basic stuff there, that you might need in a while. Like AJAX error handling, jQuery basic forms plugin usage and events handling. Also accent is on connecting exactly Django backend and jQuery.form frontend. Here it is. http://garmoncheg.blogspot.com/2013/11/ajax-form-in-django-with-jqueryform.html

Upvotes: 0

jpic
jpic

Reputation: 33420

You need JavaScript. For example, with jQuery:

$('form').submit(function() {
    $.post($(this).attr('action'), $(this).serialize(), function(data, textStatus, jqXHR){
        if (typeof data == 'object') {
            // json was returned, update the chart with the json
        } else {
            // the form had error, the form's html was returned
            $('form').html(data);
        }
    })
})

You can have such a python view:

from django.utils import simplejson
from django import shortcuts

def chart_form(request):
    template_full = 'chart_full.html' # extends base.html etc ..
    template_form = 'chart_form.html' # just the form, included by chart_full.html

    if request.method == 'POST':
        form = formClass(request.POST) 
        if form.is_valid():
            // do your chart_data
            chart_data = ....
            return http.HttpResponse(simplejson.dumps(chart_data), mimetype='application/json')
    else:
        form = formClass()

    if request.is_ajax():
        template_name = template_form
    else:
        template_name = template_full

    return shortcuts.render(template_name, {'form': form}, request)

Note: that won't work if your form contains file fields. In that case, rely on this plugin: http://jquery.malsup.com/form/ (function ajaxSubmit)

Upvotes: 1

Mariusz Jamro
Mariusz Jamro

Reputation: 31683

Why not return JSON with validation errors when form contains invalid data? Django has a Form.errors fields which contains validation errors for each field. These field can easily be converted to JSON and used to mark your inputs invalid:

Your view:

def submit_form_view(request):

    if request.method == 'POST': # If the form has been submitted...
        form = ChartForm(request.POST) 
        if form.is_valid():    # All validation rules pass
            # Return JSON response if form was OK
            response_data = {'form_saved':True, 'data':'somedata'}    
            return HttpResponse(json.dumps(response_data), mimetype="application/json")
        else:                     # Invalid data:
            # Send what were errors
            response_data = {'form_saved': False, 'errors': form.errors}
            return HttpResponse(json.dumps(response_data), mimetype="application/json")

    else:
        form = ChartForm() # An unbound form

    return render_to_response('contact.html', {
        'form': form,
    })

Javascript code:

$.ajax({
  url: '{% url someapp.submit_form_view %}',
  success: function(data) {
      if( data.form_saved ){

            $('#form input').removeClass('error'); // Form submit ok -> remove error class of all fields

            var chart_data = data.chart_data;
            // Update the chart here   

      }else{
            var form_errors = data.form_errors;

            // Display form validation errors
            for(var fieldname in form_errors) {

                var errors = form_errors[fieldname];       // Array of error strings for fieldname

                $('#form input[name="'+fieldname+']').addClass('error')

            }
      }      
  }
});

Upvotes: 1

Related Questions