}}}
'''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_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
}}}
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.
{{{
}}}
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