{{{ #!html

Ajax with Dojo and simpleJson

}}} '''Inner links''' {{{ #!html Part 1
Ajax basic form submition, Django server answer return to Ajax call.
Part 2
Handling the form when JavaScript is desactivated. }}} {{{ #!html

Ajax basic form submition, Django server answer return to Ajax call.

}}} '''Ajax form submition 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 submittion 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 registred user see the details of a recipe, the user can mark it or update their 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 the fancy effect we can add fading status message like "Your mark has been updated". The select box proposes a mark from 1 to 5, it is actually a form which is sent to the server via POST, which in django link to a method in the view.py : ''def details(request)'' which do 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 mark. 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 desactivated.

    }}} If a user has desactivated his browser javascript support, or is using a text mode browser, we need a way of making the previous marking button submit the mark 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 need 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