This is yet another Django tutorial, it makes a difference since

  • the author is totally a noob about Django programming, he has little experience on database and Web framework. You may find it interesting how a newbie benefit from the elegant design of Django.
  • each step is carefully recored via Google Code, you can check out the snapshot to understand how the project evolves.

Translations are freely permitted as long as they are released under Attribution-Noncommercial-Share Alike Creative Common License. Learning Django by Example has already been fully or partially translated into several languages. If you translate it into another language and would like to be listed here, just let me know.

Think Big

I really love the intuitive interface of Delicious Library, unfortunately, it is Mac-only, and it may not scale due to the lack of database server support. Personally I prefer a small daemon running silently in my old Gentoo box to serve contents to all clients, cross-platform and it is supposed to scale when the library grows, and I may share the resources with my friends. So I decide to brew my tea: Gelman, named after the library in the main campus of the George Washington University.

I choose Django for the python on rail hype. I just need a lightweight, pythonic Web framework. Hopefully I could enjoy the development process.

NOTE: In the following tutorial, I would show the code snapshot in each step, when I mention check r#, you can checkout the code via:

svn checkout http://gelman.googlecode.com/svn/trunk/gelman -r3 gelman

in the case, # is 3. If Google code supported Trac, that make it much easier.

Start the Engine

Installation of django-0.96 in Gentoo is like a breeze, just emerge it. And follow the official tutorial to initialize the project and setup the database, sqlite3 is used in the rest of tutorial for the sake of simplicity. When it is in production use, we would migrate it to MySQL . Check r5.

Next, add an application library:

python manage.py startapp library

Gelman is designed as a lightweight eBook management system, so it make senses to reuse existed components and Web services. For example, the meta data is from Amazon Web Service(aka AWS), the tag suggestion may come from Yahoo. Only the essential information is stored locally, such as the mapping between the meta data and eBook. The following models, Author, Publisher and Book are added.

class Book(models.Model):
    isbn13 = models.CharField(maxlength=13, primary_key=True)
    name = models.CharField(maxlength=255)
    authors = models.ManyToManyField(Author)
    pages = models.IntegerField()
    publisher = models.ForeignKey(Publisher)
    pub_date = models.DateTimeField(‘data published’)

ISBN-13 is the latest standard, we may add ISBN-10 later if necessary. Author and Book is many to many related, so use models.ManyToManyField as suggested. Finish the following boilerplate work to make it run:

  • Add django.contrib.admin to INSTALLED_APPS setting
  • Sync the change to the database
  • Edit the urls.py to enable the admin page

Now we can login to http://localhost:8000/admin using bookstack and gelman as the Username and Password, check r7.

Initially, books are added manually for a test drive, we would automate this tedious procedure later. Notice the link between the Book and Publisher, it is really cool that the admin would help us to handle the relation of models.

Add book


Add one line to the urls.py to enable the user interface for normal users:

(r‘^library/(?P<isbn>\d+)/$’, ‘gelman.library.views.detail’),

Add the detail.html in template/books as the view template, don’t forget to set TEMPLATE_DIRS variable in settings.py, then implement detail in library/views.py:

def detail(request, isbn):
    book = get_object_or_404(Book, isbn13=isbn)
    return render_to_response(‘books/detail.html’, {‘book’: book})

Now we can access the data via: http://localhost:8000/library/9780596009250/, it is just a boring raw text, we would refine it later. Check r8.

Make Librarian happier

In a real library, the user may browser, search, borrow, return books, but only librarian manage the collection. It is less fun to type all the meta data of the book while we can fetch it from Amazon. As a librarian instead of bar code reader, I prefer title/keyword than boring 13 digital numbers.

And AJAX of course, who is not using it? I will eat my own dog food, The Dojo Javascript Toolkit. You can either use dojo-0.9 release to take full advantage of AOL’s CDN(thanks, Alex) or the SVN version just as I did.

how to server the static file is probably the most frequently asked question in #django since the regular expression based URLConf more or less overkill, :-)

(r‘^static/(?P<path>.*)$’, ‘django.views.static.serve’, {‘document_root’: ‘/home/bookstack/projects/media’}),

And prepare the files as well:

cd /home/bookstack/projects/media;
mkdir scripts
sudo mount -o bind $DOJO_ROOT/trunk scripts

Now http://localhost:8000/static/scripts/dojo/dojo.js would point to the dojo.js ultimately.

Consider users may input dash and space in ISBN, we need to override the default maxlength attribute of Isbn13 and add a validation for ISBN using JavaScript:

dojo.addOnLoad(function() {
            var p = dojo.byId(“id_isbn13″);
            var n = document.createElement(“div”)
            p.parentNode.appendChild(n);

            dojo.connect(p, ‘onblur’, function() {
                if(isValidISBN(p.value)) {
                    n.innerHTML = “<div>Searching…</div>”
                } else {
                    n.innerHTML = “<div>Invalid ISBN</div>”
                }
            });
            p.removeAttribute(“maxlength”);
        });

NOTE: isValidISBN is merged into dojox.validatie.isbn, renamed as dojox.validate.isValidIsbn. That is just one of benefits to run dojo SVN.

It is a good chance to customize the admin page, a new template add.html is added:

{% block content %}
        <div id=“content-main”>
        <h1>Add Book<h1>
            <input type=“text” id=“id_isbn13″ class=“vTextField” name=“isbn13″ size=“30″ value=“”/>
            <input type=“button” id=“search_isbn” value=“Search ISBN” />
            <div id=“isbn_valid”></div>
            <input type=“text” id=“id_title” class=“vTextField” name=“title” size=“30″ value=“”/>
            <input type=“button” id=“search_title” value=“Search Title”/>
            <form action=“” method=“post” id=“book_form”> <div>
                <fieldset class=“module aligned ()” id=“books”>
                <div class=“form-row” id=“firstrow”> No book found
                </div>
                </fieldset>
                    <div class=“submit-row”>
                        <input type=“submit” value=“Save and add another” name=“_addanother”  />
                        <input type=“submit” value=“Save and continue editing” name=“_continue” />
                        <input type=“submit” value=“Save” class=“default” />
                    </div>

            </div></form>
        </div>
{% endblock %}

If we use XMLHttpRequest, we have to go through the boring XML parsing and deploy a proxy to bypass cross-domain AJAX. Neither of them is pleasant. AWS’s JSONP interface is the rescue. In dojo 0.9, dojo.io.script.get replaces the magic dojo.io.bind with Twisted-like syntax, that make it even sweeter.

dojo.connect(searchISBN, ‘onclick’, function() {
        dojo.io.script.get({
            url: ‘http://xml-us.amznxslt.com/onca/xml’,
            content: {
                Service: ‘AWSECommerceService’,
                SubscriptionId: ‘19267494ZR5A8E2CGPR2′,
                AssociateTag: ‘kokogiak7-20′,
                Operation: ‘ItemLookup’,
                Style: ‘http://kokogiak.com/amazon/JSON/ajsonSingleAsin.xsl’,
                ContentType: ‘text/javascript’,
                IdType: ‘ISBN’,
                ResponseGroup: ‘Medium’,
                SearchIndex: ‘Books’,
                ItemId: isbn.value,
            },
            callbackParamName: “CallBack”
            }).addCallback(function(response) {
                var item = response.Item;
                var row = dojo.byId(“firstrow”);
                row.textContent = null;
                var node = document.createElement(“div”);
                node.innerHTML = ‘<div> <img src="’ + item.thumburl + ‘"/> ‘ + item.title;
                row.appendChild(node);
            });
    });

NOTE: All the parameters are case-sensitive, especially CallBack for callbackParamName. Check r10. Oops, I forgot add it to the repository, so please check r11 for the updated version, further explanation is in section 2.

The above code is dojoized Kokogiak’s idea, and we still use his ajsonSingleAsin.xsl for quick prototype, we would develop a XSLT to meet our requirement in the next section.

Add book by ISBN search



19 Comments to “Learning Django by Example(1): Start the Engine”

  1. Daniel | September 4th, 2007 at 8:07 pm

    I couldn’t understand some parts of this article ning Django by Example(1): Start the Engine by Refactor the Life, but I guess I just need to check some more resources regarding this, because it sounds interesting.

  2. Learning Django by Example(2): Show me your data by Refactor the Life | September 5th, 2007 at 5:47 am

    [...] Learning Django by Example(2): Show me your data Posted on September 5th, 2007 in Web In last post, we wrapped up the different pieces and make Gelman running, we would continue keep working on the [...]

  3. Rhonabwy » Nice Django/Dojo (Ajax toolkit) tutorial | September 5th, 2007 at 9:50 pm

    [...] out perusing bookmarks and randomly following links, I stumbled across a nice “from the beginning” Django tutorial that includes a little bit of Ajax with the Dojo Toolkit. There’s also a Part 2 to the [...]

  4. Python Bytes - Today’s Top Blog Posts on Python - Powered by SocialRank | September 6th, 2007 at 6:57 am

    [...] Learning Django by Example(1): Start the Engine [...]

  5. Alex Russell | September 9th, 2007 at 6:20 pm

    Hey Kun,

    This series rocks.

    As a quick aside, you can use a .forEach() method directly on the result of a dojo.query() call. Also, users can now use Dojo 0.9 out of AOL’s CDN so they don’t need to download anything. The details are at http://build.dojotoolkit.org/0.9.0/

    Again, great stuff.

    Regards

  6. links for 2007-09-11 « PaxoBlog | September 11th, 2007 at 3:26 pm

    [...] Learning Django by Example(1): Start the Engine by Refactor the Life I really love the intuitive interface of Delicious Library, unfortunately, it is Mac-only, and it may not scale due to the lack of database server support. It is preferred to run a daemon silently in my Gentoo box and serving various clients, portable and (tags: ajax django dojo javascript json tutorial ebooks application) [...]

  7. Learning Django by Example(5): Time to Attack by Refactor the Life | September 19th, 2007 at 2:23 pm

    [...] by Example(5): Time to Attack Posted on September 19th, 2007 in Gentoo In the last four section(1, 2, 3, 4) ,we have been familiar with the environment, it is time to do some real [...]

  8. links for 2007-09-27 « Mandarine | September 26th, 2007 at 8:43 pm

    [...] Learning Django by Example Using Jjango to make a Delicious Library web clone. (tags: ajax javascript learning python webdev books library) [...]

  9. Whatever-ishere | November 21st, 2007 at 7:15 am

    thanks for the GREAT post! Very useful…

  10. Tomcask | February 4th, 2008 at 6:22 am

    in advance, sorry for my english.

    Congratulations on the article.

    I have a free Spanish translation of this article in my blog (http://tomascasquero.com/desarrolloweb/aprendiendo-django-con-ejemplos-1-arrancando/), if not to your liking, I hope your notice to remove it.

    Thanks.

  11. bookstack | February 4th, 2008 at 11:52 am

    Thanks for your notice and following the same license. Yes, I hope this serial may benefit more people whose native language is not English.

  12. Tomcask | February 5th, 2008 at 11:24 am

    Thanks for the link

  13. hollerith | February 11th, 2008 at 2:00 pm

    At the end of page I fetched r11 and tried add_book but all I got was

    lineNumber 51
    message “searchISBN is not defined”.
    name “ReferenceError”
    stack “()@http://localhost:8000/admin/library/book/add/:51\n()@http://localhost:8000/static/scripts/dojo/_base/_loader/loader.js:146\n()@http://localhost:8000/static/scripts/dojo/_base/_loader/loader.js:235\n()@http://localhost:8000/static/scripts/dojo/_base/_loader/loader.js:218\n([object Event])@http://localhost:8000/static/scripts/dojo/_base/_loader/hostenv_browser.js:233\n@:0\n”

  14. bookstack | February 11th, 2008 at 8:41 pm

    @hollerith,

    I also checked r11, and found in r11, the Search ISBN and Search Title already merged. So could you please double check whether you checked out the correct revision?

    Some other things may help:
    In settings.py, the template path is hard-coded. This design flaw has been fixed later when I migrated the repository from Google Code to Assembla.

    Also the dojo SVN may be replaced by the dojo AOL CDN. That may save some trouble for you as well.

    I must confess I am quite lousy when handling the code revision, it is so often that some file are forgotten to checkin. If this version does not work, please try the next revision, reading the SVN log and diffing between revisions may help as well. Really sorry about the inconvenience.

  15. Getting Started With… | Resource Pile | July 16th, 2008 at 1:18 pm

    [...] Web Monkey’s Getting Started with Django Kunxi.org’s Learning Django by Example(Part 1, 2, 3, 4, 5, 6, 7, [...]

  16. fornetti | August 30th, 2008 at 6:19 pm

    I do not believe this

  17. Django and 10 Best Tutorial Resources | HazelNotes | January 1st, 2009 at 8:34 am

    [...] 5. Learning Django by Example [...]

  18. 10+ Artikel, die man als Django-Entwickler gelesen haben sollte « nEws vom ConsEbt team | January 15th, 2009 at 2:35 am

    [...] the Tutorial on the Djangoproject page and the Djangobook I found “Learning Django by Example” but I am sure there are others out [...]

  19. Martinez Marchand | March 28th, 2009 at 7:22 am

    This is a nice piece of code. Can I bookmark this and then mention it on my blog?

Leave a Comment