Python and AJAX tutorial for beginners with web.py and jQuery
Note: I’m always surprised to see this at the top of a Google search for ‘ajax python’. It has been three and a half years after all (though I guess that’s how these things work sometimes). I haven’t been following the state of Python web development at all, so this code may be badly out-of-date, simply not work, etcetera. If you’re just starting out on your Pythonic path, I feel like my best recommendation is to Google on, my friend, Google on…
14 September, 2013
I’ll try to summarize what I’ve learned over the last week or so. What I was looking for was a simple way to get into this stuff, and as I discovered it’s not so simple, for a hobbyist like myself, once you combine two or three frameworks in a project. Maybe this post will help someone else in the same situation.
Python already was the choice for the server-side script. What I then needed was a way for the browser to communicate with the script. There are literally dozens of ways to do this, and the Python wiki has a good page on web programming. I also found this conversation springing from a post by the BDFL at Artima a good read, though it’s a few years old now.
I decided to keep it simple (though I still want to learn frameworks like Django, TurboGears, etcetera at some point in the future). I considered looking into using CGI with Python, but then I found web.py, a lightweight web framework, and liked it immediately.
The web.py framework can run its own basic web server or interface with others such as Apache or lighttpd. It includes a simple HTML template language that basically is just HTML + Python. It works with databases or not. In short it has all you need to get things running quickly.
OK, first let’s look at the basic project structure. Note that I’m using web.py’s templates, though this isn’t required:
Our app.py is the web.py script that controls our app. It looks like this:
import web def make_text(string): return string urls = ('/', 'tutorial') render = web.template.render('templates/') app = web.application(urls, globals()) my_form = web.form.Form( web.form.Textbox('', class_='textfield', id='textfield'), ) class tutorial: def GET(self): form = my_form() return render.tutorial(form, "Your text goes here.") def POST(self): form = my_form() form.validates() s = form.value['textfield'] return make_text(s) if __name__ == '__main__': app.run()
Other than the simple echo function defined for this tutorial, there are some critical parts to the code above, so let’s look at it more closely.
First of all web.py will map the URL to classes in the script. That’s why we have urls = (‘/’, ‘tutorial’), which says map the root URL address to what happens in the tutorial class defined later.
Second, if we’re using templates we tell web.py where to find our HTML templates with render = web.template.render(‘templates/’). The my_form creates an HTML text entry box that we’ll render later. We give it an empty string for a name simply for aesthetic reasons (otherwise you’ll see it on the HTML page).
Finally we define the tutorial class. We create the GET and POST methods ( web.py intentionally requires this)to define the behaviour of our web.py app in response to browser client requests. Looking at GET:
def GET(self): form = my_form() return render.tutorial(form, "Your text goes here.")
Defining form = my_form() gives us a copy of the form instance we created earlier. The method render.tutorial() returns a rendering of the tutorial HTML template (using the same name tutorial here in the class definition, the call, and eventually the HTML template are important here!), passing the arguments form and the text string to the template at the same time.
Now for POST:
def POST(self): form = my_form() form.validates() s = form.value['textfield'] return make_text(s)
Again using a copy of the form instance, we automatically validate it (simply because I haven’t learned how to validate forms yet!). We can then access the value of our textfield box with form.value[‘textfield’], and send it to our echo function defined earlier.
That’s all you need in the app.py, now to look at the HTML template (located in the templates directory):
(we’ll see how WP handles the syntax styling with that…)
There’s a little more going on here so let’s take it one step at a time.
In a web.py template the first line of the file defines what data is passed to the template by web.py. So here we have $def with (form, text), which as you’ll recall parallels the call we made in the GET method above. So when a client requests this page, those arguments are passed along.
It’s actually pretty simple. All this does is send what’s in the textfield box to the server when the button is clicked. If the action is successful (i.e. the server accepts the input data and returns it OK), jQuery replaces what’s in the foo element with the data returned. This uses the ajax() function — so the page doesn’t reload (it’s not really AJAX as you may realize, since there’s no XML exchanged — that’s OK, I don’t like XML anyway! ;) )
I’ll point out some conventions that a beginner should know. Saying “.button” just refers to the element with the button class (defined in our app.py script, look above). Saying “input#textfield” refers to the input element with the CSS id of textfield (also defined in app.py).
The success function passed through ajax() is called if the action is successful; the part with hide() and then fadeIn() is just a prettier way of displaying the new text on the HTML page (otherwise, it just replaces it with no transition effect).
Lastly, returning false prevents the page from reloading.
Moving on to the body of the HTML page:
<body> <br> <form class="form" method="post"> $:form.render() <input class="button" type="submit" value="send"/> </form> <br><br> <span id="foo">$text</span> </body>
All we do here is enclose a rendering of the textfield and button in form tags. This isn’t totally necessary, but as I discovered, without the form tags pressing return in the textfield box doesn’t activate the button (i.e. you have to click on the button). Apparently you can process keyboard events with jQuery but this seemed simpler.
An additional note about $ is that you need to use $: if the expression will contain some HTML that you don’t want escaped, as we do with $:form.render.
The foo span element is just a basic container that the jQuery function can replace with the new text.
There are some obvious improvements to be made here. First of all there’s nothing stopping the user from clicking the button multiple times and sending multiple POST requests (probably not a desirable situation). There also isn’t any user session setup or form validation. In any case these things are next in my list to learn, but my experience with web.py and jQuery has been great so far. I’m looking forward to more!
Also, the documentation for web.py and jQuery are good, so check those out for more information on their APIs.
I’d like to thank the members of the web.py mailing list, particularly Branko Vukelić, for their feedback on an earlier draft of this post. Naturally all errors are (most definitely) my own.
If you’d like a zipped copy of the files in this tutorial, you can get it here. Run app.py and then point your browser to http://localhost:8080 (on Windows; use the appropriate address for your setup).