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…

– George
14 September, 2013

As a result of following along with the NYU class Reading and Writing Electronic Text I started to get into using Python for web apps — this, along with learning JavaScript, is something I’d been wanting to do for a while.

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.

So I had the server language and the web framework. All that remained was the client-side. Rather than rolling all the JavaScript myself I went with the jQuery library. I had heard some good things and it looked good when I checked it out.

OK, first let’s look at the basic project structure. Note that I’m using web.py’s templates, though this isn’t required:

- webpy_jquery_ajax_tutorial
  - static
      jquery.js
     tutorial.css

  - templates
    tutorial.html
  app.py

Within the project directory we include the static and templates directories (required terms for web.py). Files such as CSS, JavaScript, images, and so on are put in static. HTML templates go in templates, and everything else can go in the main project directory.

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):

$def with (form, text)

<!doctype html>

<html>

    <head>
        <title>Hello</title>
        <link rel="stylesheet" type="text/css" href="/static/tutorial.css" />
        
        <script type="text/javascript" src="/static/jquery.js"></script>
        
        <script type="text/javascript">
                                jQuery(document).ready(function() {
                                jQuery(".button").click(function() {
                                        var input_string = $$("input#textfield").val();
                                        jQuery.ajax({
                                                type: "POST",
                                                data: {textfield : input_string},
                                                success: function(data) {
                                                jQuery('#foo').html(data).hide().fadeIn(1500);
                                                },
                                                });
                                        return false;
                                        });
                                });
        
                        </script>
    </head>
    
    <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>
    
</html>

(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.

The web.py templating language uses ‘$’ symbols to denote Python expressions (and the arguments passed into the template). To use an actual ‘$’ just escape it with ‘$$’. Note that it’s a popular habit in jQuery to also use the dollar sign to mean the jQuery object, but we can get around that conflict simply by using ‘jQuery’ instead in the JavaScript. There are other ways around that but I like this method for its simplicity and how it feels the same as Python namespaces.

The head tag of the file is nothing special, it just tells us where the CSS and .js files are located (though it’s important to web.py that they’re in static as explained before). The JavaScript that follows needs some more explanation though:

        
        <script type="text/javascript">
                                jQuery(document).ready(function() {
                                jQuery(".button").click(function() {
                                        var input_string = $$("input#textfield").val();
                                        jQuery.ajax({
                                                type: "POST",
                                                data: {textfield : input_string},
                                                success: function(data) {
                                                jQuery('#foo').html(data).hide().fadeIn(1500);
                                                },
                                                });
                                        return false;
                                        });
                                });
        
                        </script>

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.

To recap, what this does is display (from your browser GETting the page from the server) a HTML page with a textfield and button. When you enter text in the textfield and submit it (that is, POST it), the JavaScript on the HTML page runs, and replaces the foo element with the text processed and returned by the app.py without reloading the page. There you have it — basic Python and AJAX!

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).

34 comments so far

  1. Greg on

    very good tutorial – keep up the good work!

  2. agricola on

    Your link to web.py is incorrect – it should be http://webpy.org/

    Great tutorial, though :)

  3. georgek on

    Ouch — thanks agricola!

  4. [...] the original here: Python and AJAX tutorial for beginners with webpy and jQuery … Posteado por: yoobz on March 1, [...]

  5. Bas Konig on

    Typo in the down-loadable example CSS: there is a line
    background-color:#FBFFF;

    should be –>>
    background-color:#FBFFFF;

  6. Basavanagowda Kanur on

    Thank you very much, very good tutorial.

  7. hide1713 on

    I think there’s a bug in your code

    $$(“input#textfield”).val();

    I remove the first $ and everything work out ok.

    • Greg on

      in webpy/templator you need the $$ to escape the ‘$’ for the call in jquery

  8. Ben Racine on

    Thanks so much… this is exactly what I was looking for.

  9. thupten on

    useful info. thanks..soon:)

  10. Guillermo Siliceo Trueba on

    What i liked the most about your tutorial is how you kept things as simple as posible, often times i see tutorials where they try to be clever about code or they try to teach multiple things at once and you don’t get anything, but this is awesome, i loved the recap at the end, it really helps sum up all the concepts. I wish programming books were written this way.

  11. georgek on

    Thanks for the kind words Guillermo, I’m happy you found it useful.

  12. Alex on

    your zipped copy of files is not valid any more. Could I get it from other places? thanks.

  13. Jon Roland on

    I want something, such as a jquery script, that executes a python script
    that does nothing more than immediately display a line of HTML chosen
    based on the url of the calling HTML page, immediately upon page open or
    refresh, a different line for each URL. Initially, it can show just the
    URL, but the eventual solution is to use the URL as a dictionary key to
    select the line as a value of that dictionary.

    mod_python is not an option as we do not have access to Apache, nor
    is xBitHack to enable server-side includes. Available tools are
    javascript and python.

    Some background. This is simple javascript to display the calling url:

    var myurl = document.location.href;
    document.write(“” + myurl + “”);

    And I know how to create a link that opens a page to execute the .py script when one clicks on it:

    Here

    Here is the python script so far:

    #!/usr/bin/env python# This outputs a copyright notice to a web page
    import cgiprint “Content-Type: text/html\n”
    form = cgi.FieldStorage()

    thisurl = form.getvalue(“myurl”)
    print “””

    “””
    print “””
    Copyright © 1995-2011 Constitution Society. Permission granted to copy with attribution for non-profit purposes.
    “””
    print “””

    “””
    print thisurl

    But one has to click on “Here”. I want to have the copyright notice to appear immediately.

    The problem is that each page needs its own copyright notice, which
    is subject to change as years are added to the range of each.

    One approach to getting the referring URL can be done with

    import os
    print os.environ['HTTP_REFERER']

  14. doug on

    thanks for doing this–well constructed and thoroughly explained tutorial. Very helpful for me.

    [I did have one problem which had nothing to do with your tutorial but might be worth passing along to others--if, like me, you immediately remove the jQuery script from the html/template and put it it its own file, referenced by 'src' attribute in the tag, remember to remove one of the two consecutive "$"in line 16 above, because it is no longer necessary to escape the jQuery symbol '$'. This was difficult to debug--but again, no fault in the tutorial.]

  15. paurullan on

    Thanks for the work!

  16. Giuliano Lancioni on

    Thank you for your wonderful work.

    However, I got a couple of problems in running the example on browsers other than Firefox. Chrome doesn’t seem to be able to load the css (in python 2.5; it seems to work fine in python 2.6), so everything is fine but styles are not applied.

    A more serious problem happens with IE7 (it seems, with any version of python): first, it complains against having a ‘,’ without a second argument in jQuery.ajax function (easy to fix: suffices to leave the ‘,’ out); second, more serious, it doesn’t accept xhtml-style short end tags such as .

    While I can fix the problem in the template by replacing the final ‘/>’ with ‘>’ and adding a tag, I found no way to compel jQuery to output html-style tags in dynamically produced html.

    Is there a way I can “persuade” jQuery to produce old-style tags?

    Thanks!

  17. georgek on

    Hi Giuliano, good question. This tutorial is old enough that it would be worth redoing it with the most recent versions of jQuery, web.py, etcetera and then testing it against browsers. I’m sure some cruft has accumulated in the last two years but I don’t have plans to update this example.

    One of the pitfalls of online tutorials is they can go badly out of date with a beginner being none the wiser, so if you were to take that on and get a good complete example working I’d be happy to link to you at the top of this tutorial.

    • Giuliano Lancioni on

      Thank you for your answer. In fact the problem lies in web.py code itself, since the form.py returns what follows in the render method of the Input class:

      return ” % attrs

      To make it compatible with IE6 it suffices to delete the ‘/’ before the closing ‘>’ here and in other similar statements.

      As to putting a better example, if I become smart enough to have something I’ll send you, but I have some doubts I’ll do!

  18. gmanghi on

    Hi everyone,

    I’m trying to run the code above by running first the app.py via console, but I was not able to because there was an error that says No module named web.

    Please help.

    thanks
    gln

  19. Giuliano Lancioni on

    web is the web.py library. Go to webpy.org, download the library and install it!

    • gmanghi on

      Thanks for your immediate reply Giuliano. :)

      I already downloaded the web.py but everytime I run the code “python setup.py install” it gives me an error that says No module named web.

      Hope to hear your response again sir.

      Thanks

  20. manbar on

    thanks for your tutorial but i have an error when i try it:

    File “c:\dev\ajax\app.py”, line 7, in
    render = web.template.render(‘/templates’)
    File “c:\dev\ajax\web\template.py”, line 1033, in __init__
    self.mod = __import__(name, None, None, ['x'])
    ValueError: Empty module name

    i installed web.py and in the directory c:\dev\ajax i have the templates directory….
    please help
    thanks

    • georgek on

      hi manbar, I’m sorry, I’m not supporting the tutorial at this point so you’re mostly on your own. However your directory structure does look a little weird. It seems like you unzipped the contents into dev\ajax\? Are you able to import web.py from Python? Check this out,

      Directory of C:\Documents and Settings\george\My Documents\downloads\tutorial

      05/23/2012 05:54 PM .
      05/23/2012 05:54 PM ..
      03/01/2010 05:02 PM 616 app.py
      05/23/2012 05:54 PM static
      05/23/2012 05:54 PM templates
      03/01/2010 05:07 PM 13,011 tutorial.txt
      2 File(s) 13,627 bytes
      4 Dir(s) 16,545,730,560 bytes free

      C:\Documents and Settings\george\My Documents\downloads\tutorial>Python app.py
      http://0.0.0.0:8080/
      127.0.0.1:3067 – - [23/May/2012 17:56:20] “HTTP/1.1 GET /” – 200 OK
      127.0.0.1:3067 – - [23/May/2012 17:56:20] “HTTP/1.1 GET /static/tutorial.css” -
      200
      127.0.0.1:3068 – - [23/May/2012 17:56:20] “HTTP/1.1 GET /static/jquery.js” – 200
      127.0.0.1:3068 – - [23/May/2012 17:56:20] “HTTP/1.1 GET /favicon.ico” – 404 Not
      Found
      127.0.0.1:3071 – - [23/May/2012 17:56:39] “HTTP/1.1 POST /” – 200 OK

      I just downloaded the tutorial, unzipped it, and then the transcript above is what I did. The log info is me successfully posting to the form box.

      So the tutorial works, there’s just a problem with your installation somewhere. Hope that helps.

  21. George Varghese on

    I have requirement to call python function from a jquery modal popup. Is it possible in web.py? I just described the req.. I have a python file.. in this file , I have added a function to add row to sqlite db.

    Then, I have a html page with ‘add’ button. When user shall click this button, display the pop up dialog.. .. I have to call the python function from here..

    If anybody, known about htis, pleas help me..

    Regards,
    George

  22. wim de bok on

    Thank you for this very nice tutorial. It’s an interesting combination of lanuages and tools you are using. Vert basic. Python, i start to love it more and more. web.py is the next stop.

  23. William on

    if you want someone to “debug” your code…just ask urrr

  24. Mahboob on

    Hi,

    After this command “python -m CGIHTTPServer” I can access it via this URL: http://localhost:8000/templates/tutorial.html

    it seems not working as expected. I can see “send” button on browser but with this piece of code (from tutorial.html)

    $def with (form, text)
    $:form.render()
    $text

    Any help would be appreciated.. Thanks.

    • Mahboob on

      When I’ve pressed “send” button then following Error message has been shown up:

      “Error response

      Error code 501.

      Message: Can only POST to CGI scripts.

      Error code explanation: 501 = Server does not support this operation.”

      is there anyone who has the similar problem ?

    • Mahboob on

      I am new to Python, I am trying to access .html page like we do in PHP i.e. localhost/tutorial.html, but i am not sure if it is right way or not.

      If i try to access app.py (mentioned in this tutorial) i.e. localhost/app.py then all the source code of app.py shown on browser :@

      I don’t know what’s going on…

  25. gvvtech on

    Hi,

    Is it possible to access template variable value in javascript?

    Regards,
    George

  26. chaiyachaiya on

    Thx buddy, i was looking for a way to escape the $ sign in javascript function.

  27. eb on

    George — Almost 4 years later, your post is still useful. I just did my first AJAX/Python exercise with your help. Thanks!

  28. Orlando yoga retreats on

    Good response in return of this issue with solid arguments
    and describing everything about that.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: