{{{ #!html

Ajax with Dojo and simpleJson

}}} '''Inner links''' {{{ #!html Part 1
Ajax basic form submission, Django server answers Ajax call.
Part 2
Handling the form when JavaScript is deactivated. }}} {{{ #!html

Ajax basic form submission, Django server answers Ajax call.

}}} '''Ajax form submission using Dojo for the client side javaScript toolkit, and simpleJson for the client/server communication.''' This will take you through all the steps required to get started and to develop a handy form submission without reload. == What do you need == - Django - Dojo (v0.3) [http://dojotoolkit.org/] an open source javascript toolkit. - Simple_Json (v1.3) [http://svn.red-bean.com/bob/simplejson/tags/simplejson-1.3/docs/index.html] used for javascript <-> python communication. == What do we want to achieve == I will use the model of my current website, a recipe one. When a registered user sees the details of a recipe, the user can rate (mark) it or update their rating (mark) by selecting a small drop down list. But when the user clicks ok the whole page reloads. We will use some Ajax to make it transparent and for fancy effect we can add a fading status message like "Your rating has been updated". The select box proposes a rating from 1 to 5, it is actually a form which is sent to the server via POST, which in django links to a method in the view.py : ''def details(request)'' which does the innerwork for the details page generation. == Installing Json == get the SimpleJson from svn. {{{ svn co http://svn.red-bean.com/bob/simplejson/trunk/ json cd json sudo python setup.py install }}} == Django part == '''view.py''' {{{ #!python def details(request): [more stuff] if request.POST: # Get all the mark for this recipe # its not the best way i sould hava an oter entry or table, with total and nbr of marks for # each recipe. list_mark = Mark.objects.values('mark').filter(recipe__pk=r.id) # loop to get the total total = 0 for element in list_mark: total+= element['mark'] # round it total = round((float(total) / len(list_mark)),1) # update the total r.total_mark= total # save the user mark r.save() # Now the intersting part for this tut import simple_json # it was a french string, if we dont't use unicode # the result at the output of json in Dojo is wrong. message = unicode( message, "utf-8" ) # jsonList = simple_json.dumps((my_mark, total, form_message ,message)) return HttpResponse(jsonList) [more stuff, if not POST return render_to_response('recettes/details.html' ...] }}} '''url.py''' Just normal url.py, remember the path which will point to the wanted method. {{{ #!python from django.conf.urls.defaults import * urlpatterns = patterns('', [...more...] (r'^recettes/(?P[-\w]+)/(?P[-\w]+)/$', 'cefinban.recettes.views.details'), [...more...] }}} == Html template with Dojo javascript == '''Dojo use''' {{{ {% load i18n %} {% extends "base.html" %} {% block script %} }}} the following ''HTML code'' just comes after the upper code snipset. {{{ [... total mark get updated, lets put css id="mark_total" ...] {% if r.total_mark %}
  • Score: {{ r.total_mark }}/5
  • {% endif %} [....] {% if not user.is_anonymous %} {% ifnotequal user.id r.owner_id %}
    {{mark_message}}

    {{ mark_status }} {% endifnotequal %} {% endif %} }}} And, voila. To have a demo use guest as login, guest as password here [http://ozserver.no-ip.com:345] or if not working here [http://www.cefinban.net]. Go to index and pick a recipe, update the rating. You can also have a look at the screenshot here : [http://ozserver.no-ip.com/~greg/images/ajaxdjango.png] == Dreamhost and Simplejson == If you are using dreamhost for hosting please be aware that simplejson is not installed. Instead you will have to install the source of simplejson in a folder in your home directory eg /proz/json/simple_json The simple_json directory contains the required __init__.py for it to be loaded as a python module. Then in your ~/.bash_profile add the directory to your python path like below. {{{ export PYTHONPATH=$PYTHONPATH:$HOME/django/django_src:$HOME/django/django_projects:$HOME/progz/json }}} That will allow yout to use simpl_json in python shell. But '''dont forget''' to change django.fcgi ! Add {{{ sys.path +=['/home/coulix/progz/json'] }}} log out/in and try import simple_json (or simplejson depends on what source you picked) {{{ #!html

    Handling the form when JavaScript is deactivated.

    }}} If a user has deactivated his browser's javascript support, or is using a text mode browser, we need a way of making the previous rating button submit the rating to the server which should this time return an html template instead of data to the Ajax call. == Updating the form HTML (details.html template)== This time we put a submit type input inside the form instead of the button type in part 1. type="submit" as indicates its name, submits the form to the server, we will need a way of stopping this behavior using javaScript. {{{
    {{mark_message}}
    }}} Now, how can we tell our details method in view.py to know if it comes from a normal submit request or an Ajax request ? Two solutions, The '''first''' uses a hidden variable in form.html and an added content element in the JS part. {{{ function sendForm() { dojo.byId("mark_status").innerHTML = "Loading ..."; dojo.io.bind({ url: '.', handler: sendFormCallback, content: {"js", "true"}, formNode: dojo.byId('myForm') }); } [...]
    [...]
    }}} With this, in our django method in view.py we can test for request["js"]=="true" it would means that Js is activatd and we return the appropriate answer to the Ajax request. The '''second''' uses the url to pass a new variable ajax_or_not to the detail method. {{{ #!python def details(request, r_slug, r_cat, ajax_or_not=None): [...] }}} We modify the url.py to accept this new parameter. {{{ #!python (r'^recettes/(?P[-\w]+)/(?P[-\w]+)/(?P.*)$', 'cefinban.recettes.views.details'), }}} The dojo binding needs to append a variable to the original document url, to make ajax_or_not not None. {{{ function sendForm() { dojo.byId("mark_status").innerHTML = "Loading ..."; dojo.io.bind({ url: './ajax/', handler: sendFormCallback, formNode: dojo.byId('myForm') }); } }}} == New details method in view.py == We just need to test for the existence of ajax_or_not {{{ #!python def details(request, r_slug, r_cat, ajax_or_not=None): [...] if request.POST: [...] same as part 1 # except here we check ajax_or_not if ajax_or_not: # use json for python js exchange # it was a french string, if we dont't use unicode # the result at the output of json in Dojo is wrong. message = unicode( message, "utf-8" ) jsonList = simple_json.dumps((my_mark, total, form_message ,message)) return HttpResponse(jsonList) return render_to_response('recettes/details.html', {'r': r, 'section':'index', 'mark_status':message , 'mark_message':form_message, 'my_mark': my_mark}, context_instance=RequestContext(request),) }}} Any troubles : coulix@gmail.com