| 1 | '''Ajax form submition using Dojo for the client side JavaScript library, and SimpleJson for the client/server communication.''' |
| 2 | |
| 3 | As others i was hearing more and more about Ajax, didn't know what it was nor what it does. |
| 4 | When i realised it was using JavaScript, it first surprised me since i thought JS was evil. Anyway, now i changed my mind, and i will take you throught all the steps recquired to get started and to develop a handy form submition without reload. |
| 5 | |
| 6 | |
| 7 | == What do you need : == |
| 8 | |
| 9 | - Django |
| 10 | - Dojo (v0.3) [http://dojotoolkit.org/] an open source javascript toolkit. |
| 11 | - Simple_Json (v1.3) [http://svn.red-bean.com/bob/simplejson/tags/simplejson-1.3/docs/index.html] used for java <-> python communication. |
| 12 | |
| 13 | |
| 14 | == What do we want to achieve == |
| 15 | I will use the model of my current website, a recipe one. |
| 16 | When a registred user see the details of a recipe, he can mark it or update his mark by selection a small drop down list. |
| 17 | That's nice but when he cliks ok the whole page is reloaded :/ , let's use some Ajax to make it transparent, and for the fancy effect, |
| 18 | we can add fading status message like "Your mark has been updated". |
| 19 | 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 |
| 20 | in the view.py : ''def details(request)'' which do the innerwork for the details page generation. |
| 21 | |
| 22 | |
| 23 | == Installing Json == |
| 24 | get the SimpleJson from svn. |
| 25 | |
| 26 | {{{ |
| 27 | svn co http://svn.red-bean.com/bob/simplejson/trunk/ json |
| 28 | cd json |
| 29 | sudo python setup.py install |
| 30 | }}} |
| 31 | |
| 32 | == Django part == |
| 33 | '''view.py''' |
| 34 | {{{ |
| 35 | def details(request): |
| 36 | [more stuff] |
| 37 | if request.POST: |
| 38 | # Get all the mark for this recipe |
| 39 | # its not the best way i sould hava an oter entry or table, with total and nbr of marks for |
| 40 | # each recipe. |
| 41 | list_mark = Mark.objects.values('mark').filter(recipe__pk=r.id) |
| 42 | # loop to get the total |
| 43 | total = 0 |
| 44 | for element in list_mark: |
| 45 | total+= element['mark'] |
| 46 | # round it |
| 47 | total = round((float(total) / len(list_mark)),1) |
| 48 | # update the total |
| 49 | r.total_mark= total |
| 50 | # save the user mark |
| 51 | r.save() |
| 52 | |
| 53 | # Now the intersting part for this tut |
| 54 | import simple_json |
| 55 | # it was a french string, if we dont't use unicode |
| 56 | # the result at the output of json in Dojo is wrong. |
| 57 | message = unicode( message, "utf-8" ) |
| 58 | # |
| 59 | jsonList = simple_json.dumps((my_mark, total, form_message ,message)) |
| 60 | return HttpResponse(jsonList) |
| 61 | [more stuff, if not POST return render_to_response('recettes/details.html' ...] |
| 62 | }}} |
| 63 | |
| 64 | '''url.py''' |
| 65 | |
| 66 | Just normal url.py, remember the path which will point to the wanted method. |
| 67 | {{{ |
| 68 | from django.conf.urls.defaults import * |
| 69 | |
| 70 | urlpatterns = patterns('', |
| 71 | [...more...] |
| 72 | (r'^recettes/(?P<r_cat>[-\w]+)/(?P<r_slug>[-\w]+)/$', 'cefinban.recettes.views.details'), |
| 73 | [...more...] |
| 74 | }}} |
| 75 | |
| 76 | |
| 77 | == Html template with Dojo javascript == |
| 78 | '''Dojo use''' |
| 79 | |
| 80 | {{{ |
| 81 | {% load i18n %} |
| 82 | {% extends "base.html" %} |
| 83 | {% block script %} |
| 84 | <script type="text/javascript" src="/media/js/dojo/dojo.js"></script> |
| 85 | <script type="text/javascript"> |
| 86 | |
| 87 | dojo.require("dojo.widget.Tooltip"); |
| 88 | dojo.require("dojo.fx.html"); |
| 89 | dojo.require("dojo.event.*"); |
| 90 | dojo.require("dojo.json"); |
| 91 | |
| 92 | // point to the same url details.html |
| 93 | function sendForm() |
| 94 | { |
| 95 | dojo.io.bind({ |
| 96 | url: '.', |
| 97 | handler: sendFormCallback, |
| 98 | formNode: dojo.byId('myForm') |
| 99 | }); |
| 100 | } |
| 101 | |
| 102 | function sendFormCallback(type, data, evt) |
| 103 | { |
| 104 | if (type == 'error') |
| 105 | alert('Error when retrieving data from the server!'); |
| 106 | else |
| 107 | // de code the simpleJson answer from django ''details'' method. |
| 108 | // it populates a Js array ! straigth so cool ! |
| 109 | arrayData = dojo.json.evalJson(data); |
| 110 | // now we update the html using the css id as a pointer to the part |
| 111 | // we want to update |
| 112 | dojo.byId("mark_total").innerHTML = arrayData[1]; |
| 113 | dojo.byId("mark_message").innerHTML = arrayData[2]; |
| 114 | dojo.byId("mark_status").innerHTML = arrayData[3]; |
| 115 | // and the fancy fading effect |
| 116 | dojo.lfx.html.highlight("mark_status", [255, 151, 58], 700).play(300); |
| 117 | } |
| 118 | |
| 119 | function init() |
| 120 | { |
| 121 | var sendFormButton = dojo.byId('sendFormButton'); |
| 122 | dojo.event.connect(sendFormButton, 'onclick', 'sendForm') |
| 123 | } |
| 124 | |
| 125 | dojo.addOnLoad(init); |
| 126 | |
| 127 | </script> |
| 128 | }}} |
| 129 | |
| 130 | the following ''HTML code'' just comes after the upper code snipset. |
| 131 | |
| 132 | {{{ |
| 133 | [... total mark get updated, lets put css id="mark_total" ...] |
| 134 | {% if r.total_mark %}<li><b>Score</b>: <span id="mark_total">{{ r.total_mark }}</span>/5</li>{% endif %} |
| 135 | [....] |
| 136 | {% if not user.is_anonymous %} |
| 137 | {% ifnotequal user.id r.owner_id %} |
| 138 | <form enctype="multipart/form-data" id="myForm" method="post"> |
| 139 | <span id="mark_message">{{mark_message}}</span> |
| 140 | <select name="select_mark"> |
| 141 | <option value ="1" {% ifequal my_mark 1 %} selected {% endifequal %}>1</option> |
| 142 | <option value ="2" {% ifequal my_mark 2 %} selected {% endifequal %}>2</option> |
| 143 | <option value ="3" {% ifequal my_mark 3 %} selected {% endifequal %}>3</option> |
| 144 | <option value ="4" {% ifequal my_mark 4 %} selected {% endifequal %}>4</option> |
| 145 | <option value ="5" {% ifequal my_mark 5 %} selected {% endifequal %}>5</option> |
| 146 | </select> |
| 147 | </form> |
| 148 | <button id="helloButton">Notez</button> |
| 149 | <br/> |
| 150 | <span id="mark_status">{{ mark_status }}</span> |
| 151 | {% endifnotequal %} |
| 152 | {% endif %} |
| 153 | }}} |
| 154 | |
| 155 | And, voila. |
| 156 | |
| 157 | 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]. |
| 158 | Go to index and pick a recipe, update the mark. |
| 159 | |
| 160 | You can also have a look at the screenshot here : [http://ozserver.no-ip.com/~greg/images/ajaxdjango.png] |
| 161 | Hope it was useful. |
| 162 | |
| 163 | I am using dreamhost for hosting, and no sinplejson is installed. I will ask them to do so, if any one knows a way of installing and using it without being |
| 164 | root ? contact me. coulix@gmail.com |
| 165 | |
| 166 | Gregory Tappero. |
| 167 | |
| 168 | |
| 169 | |