Views, URLs, and Generic Views in Django 1.0

Exclusive offer: get 50% off this eBook here
Django 1.0 Template Development

Django 1.0 Template Development — Save 50%

A practical guide to Django template development with custom tags, filters, multiple templates, caching, and more

$23.99    $12.00
by Scott Newman | July 2009 | Web Development

Many developers new to Django get tripped up on the vocabulary and purpose of different pieces of the system—models, views, generic views, model managers, and so on. With some functions belonging to models and others to views, it can be confusing to know where to put the logic of your applications.

The view is where most of your application logic will be executed. Before we can work with views, however, we need to look at the URL dispatching system to see how a view is matched up with an incoming request. Once we have seen the URL dispatcher and some working views, we'll take a look at some shortcuts Django offers us to accomplish these actions even more quickly.

You can write entire Django sites without using models, but you'd have a hard time doing that without views or generic views.

In this article by Scott Newman, we will:

  • Create a sample application to work with
  • Learn how the URL dispatcher works and how URLs are matched to views
  • Explore the structure of views
  • Build views to display a list of content and content detail
  • See how to cut down development time with generic views
  • Examine when to use regular views instead of generic views

An overview

Views are at the heart of Django and hold most of your application logic. They are nothing more than Python functions that take an HTTP request as input and return an HTTP response or error.

A mechanism called the dispatcher identifies an incoming URL against a set of URL patterns and their associated view functions. When a match is found, the associated view is called and the request gets handled.

Since many views follow a common strategy of loading an object or list, loading a template, rendering the template, and returning a response, Django offers a way of doing this without writing a view function. These generic views are called from the URL dispatcher and go right to the template.

Creating the application

Before we start looking at views and URLs, let's create a sample application to experiment with. Since most books and examples use blog models as their demos, let's keep things fresh by making our demo a press release application for a company website. The press release object will have a title, body, published date, and author name.

Create the data model

In the root directory of your project (in the directory projects/mycompany), create the press application by using the startapp command:

$ python manage.py startapp press

This will create a press folder in your site. Edit the mycompany/press/models.py file:

from django.db import models

class PressRelease(models.Model):
title = models.CharField(max_length=100)
body = models.TextField()
pub_date = models.DateTimeField()
author = models.CharField(max_length=100)

def __unicode__(self):
return self.title

Create the admin file

To take advantage of the automatic admin interface that Django gives us, we need to create a file called an admin file. Create a file called admin.py in the mycompany/press directory, adding these lines:

from django.contrib import admin
from mycompany.press.models import PressRelease

admin.site.register(PressRelease)

If you've used Django before version 1.0, this step is new. The admin configuration directives were taken out of the model and put into their own files starting in version 1.0.

Add the press and admin applications to your INSTALLED_APPS variable in the settings.py file:

INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.admin',
'django.contrib.contenttypes',
'djan?go.contrib.sessions',
'django.contrib.sites',
'mycompany.press',
)

In the root directory of your project, run the syncdb command to add the new models to the database:

$ python manage.py syncdb

Because we have Django's authentication system listed as one of our installed applications, the initial syncdb process will ask us if we want to create a superuser. Go ahead and create a superuser account; you will be using it later to access the admin site.

Configure the URLs

Finally, edit the mycompany/urls.py file:

from django.conf.urls.defaults import *
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
)

Add data in the admin application

By adding django.contrib.admin to our INSTALLED_APPS setting and creating a URL mapping for it, we can access the admin site by browsing to http://localhost:8000/admin/.

Go into the admin app and add two or three press releases so that we have some sample data to work with:

Views, URLs, and Generic Views in Django 1.0

Mapping URLs to views

When Django accepts an incoming request, one of the first things it does is that it looks at the URL and tries to match it against a group of URL patterns. In order to identify patterns, Django uses regular expressions to see if the URLs follow a known format.

Consider these URLs:

http://localhost:8000/press/detail/1/

http://localhost:8000/press/detail/2/

These URLs appear to follow a pattern that they start with press/detail/ and end with a number that represents the ID of a press release. (Recall that we don't work with the domain name portion of the URL. Django takes care of this automatically for us and just sends us everything that follows the domain name.)

With this pattern, we can add a new line to our mycompany/urls.py file:

from django.conf.urls.defaults import *
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^press/detail/d+/$', 'mycompany.press.views.detail'),
)

If you're not familiar with Python's regular expressions, this new line may look a bit wonky. This is the most important part:

r'^press/detail/d+/$'

It reads like this: "A string that starts with press/detail/ and ends with one or more digits followed by a slash".

The second segment of the new line is the view function that will get called when an incoming URL matches this pattern. In this case, it will be a function called detail in the mycompany/press/views.py file.

There's only one problem with this pattern—it recognizes that a number will be at the end of the URL, but doesn't do anything to pass that number to the view when it's called.

We can use a Python regular expression group to capture that number:

urlpatterns = patterns('',
(r'^admin/', include('django.contrib.admin.urls')),
(r'^press/detail/(?P<pid>d+)/$',
'mycompany.press.views.detail'),
)

This grouping syntax looks really funky, but it's easy to understand once you've seen it a few times. (?P ) is the Python syntax for a named group, which allows the regular expression to save the piece that matched, and put a label on it so that we can call it later. The <pid> part is where we assign the label of pid to the ID of the press release that was sent with the URL.

In the case of this URL, the named group pid will be equal to 2:

http://localhost:8000/press/detail/2/

Any named groups that we get from a URL are passed as arguments to our view function. In this example, our detail function in press/views.py will have a method signature like this:

def detail(request, pid):
p = PressRelease.object.get(id=pid)
..

There are two keyword arguments to the detail function, request and pid. (Django automatically passes the keyword request, which we'll explore a little later.)

Because we used a named group in the URL configuration to capture the press release ID, it's passed to our detail function as pid. You can use multiple named groups in your URL patterns to capture multiple pieces of information and pass them to your functions.

Note: URL configurations and patterns are usually referred to as URLConf.

Handling unmatched URL patterns

URLs are matched up with view functions when they match patterns, but what happens when a match isn't found? This URL wouldn't match the patterns we created because it doesn't end in a number:

http://localhost:8000/press/detail/abc/

In this case, the URL dispatcher wouldn't match against our pattern and would keep trying other patterns until a match is found. If no match is found, a 404 error is raised. If you have debug set to true (DEBUG=True) in your settings file, you'll see an error message like this:

Splitting up the URL configurations

We created the URL configurations for the press application in the mycompany/urls.py file. While this is perfectly acceptable, sticking all the configurations into the main urls.py file can get unwieldy for large projects with many applications. It also isn't very modular if we want to share applications with others or use applications that other people distribute.

Instead of writing the press release configuration in our main mycompany/urls.py file, let's create a new file at mycompany/press/urls.py:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'^detail/(?P<pid>d+)/$', 'press.views.detail'),
)

This looks very similar to what we already have, but note that we've dropped press from the beginning of the regular expression. This line will match URLs that start with detail.

Open your mycompany/urls.py file and edit the highlighted line:

from django.conf.urls.defaults import *
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^press/', include('mycompany.press.urls')),
)

We've changed the regular expression portion to match URLs that start with press/. If one is found, Django will hop over to the press/urls.py file to try to match the rest of the URL (without the press/ prefix).

With this setup, we are telling Django that any URLs that start with press will be handled in a separate urls.py file in the press directory.

Creating views

Now that we're matching a URL to a view and passing it information, we can look at how a view is structured. Views have two rules you must follow:

  1. The view must accept the request object as its first argument.
  2. The view must return an HTTP response or an exception.

Beyond this, just remember that a view is a standard Python function and you can do just about anything in it that you can do in a Python program.

Accepting the request object

Our first rule for views states that a view must accept the request object as its first argument. What is this request object?

Django automatically creates the request object when a page is requested. It contains data about the incoming HTTP request such as the requestor's IP address, user agent, request method, cookies, GET parameters, POST parameters, and so on. Everything you should need to know about an incoming request will be found in this object.

When you build your view functions, always specify request as the first keyword argument:

def detail(request):
# Python code here

If you forget to add request as the first parameter, you'll know quickly because your view will fail to load with some kind of error message about the arguments (the exact error depends on what other keyword arguments you might be using).

Responding with an HTTP response

The second rule for views is that a view must return an HTTP response or an exception. Let's start by talking about what an HTTP response is.

In order for a browser to understand how to render a web page, it looks at some special hidden information called headers, which is sent by the server along with the content or document being requested. These headers tell the browser information such as what kind of web server is sending the response, which version of the HTTP protocol is being used, how big the content is, and what kind of content is being sent.

Luckily, we don't have to worry about most of this because the web server and Django take care of it for us. All we have to do is make sure we send the response out of our view using the HttpResponse method.

In your mycompany/press/views.py file, add the following lines:

from django.http import HttpResponse

def detail(request, pid):
return HttpResponse('This is just a test.')

Point your browser to http://localhost:8000/press/detail/1/. Here's what it should look like:

Views, URLs, and Generic Views in Django 1.0

Obviously, our views are going to be more complicated than this one, but it illustrates how simple they can be.

Responding with an exception

The second part of our rule said that the view can respond with an exception instead of an HTTP response. When Django encounters an error during the processing of a view, we usually want to return a friendly error message to the user to let them know something went wrong (as opposed to just sending back a blank screen). Usually, these error messages are in the form of 404 or 500 Error pages.

404 errors are also known as page not found errors. Anyone who has spent time surfing the Web has undoubtedly encountered a 404 Error page when clicking an old link that is no longer valid. In traditional HTML publishing, 404 errors popped up when the user requested a filename that wasn't found on the server (that's where the "page" in "page not found" comes from). With Django, we don't have URLs that represent filenames on the server, but we still return a 404 error when the user is looking for a resource that does not exist.

Django makes it easy to return a 404 page by returning the error using the HttpResponseNotFound function:

from django.http import HttpResponseNotFound

def detail(request, pid):
return HttpResponseNotFound('Page Not Found')

Similarly, requests that cause errors on the server are usually referred to as 500 errors. (500 is the standard HTTP response code for a server error.) Django also makes it easy to serve a 500 error:

from django.http import HttpResponseServerError

def detail(request, pid):
return HttpResponseServerError('An Error Has Occurred.')

Putting the views together

Now that we know how a view works and what it needs to do, let's write the real view to work with our sample application.

Building the basic view

In your mycompany/press/views.py file, replace any contents with the following lines:

from django.http import HttpResponse
from django.http import HttpResponseNotFound
from mycompany.press.models import PressRelease

def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
try:
p = PressRelease.objects.get(id=pid)
return HttpResponse(p.title)
except PressRelease.DoesNotExist:
return HttpResponseNotFound('Press Release Not Found')

If you'd like to test it out, point your browser to http://localhost:8000/press/detail/1/. You should see the title of your press release. Change the number at the end of the press release to an ID that doesn't exist (such as 99) and you should get a Page Not Found error.

This view doesn't return a very pretty output, but it follows the rule that the view must serve an HTTP response or an error/exception. The try/except error handling to make sure the press release exists is kind of ugly. Luckily, Django gives us a more elegant way of handling it.

Cleaning up the error handling

Instead of putting a try/except block around the object lookup, Django has a get_object_or_404 method that will automatically raise an error if the object is not found.

Change the highlighted lines in your mycompany/press/views.py file:

from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from mycompany.press.models import PressRelease

def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=pid)
return HttpResponse(p.title)

That's a much cleaner way of doing things!

Note: If you're getting a list instead of an object, Django has a get_list_or_404 method that you can use. We'll see this in a few pages.

Adding the template files

The last thing we need to do is add a way to load up the response with the output of a rendered template.

We're going to load a template file, replace placeholders in that file with our data (called "rendering" the template), and then return the contents of the template as a string as an HTTP response.

We create a templates directory at mycompany/templates, and configured the settings.py file to tell Django where to find it:

TEMPLATE_DIRS = (
'/projects/mycompany/templates/',
)

Verify that you have configured your project this way before continuing. With this setting in place, we can load templates relative to this path.

Create a directory under the mycompany/templates directory called press. (It's common practice to use subdirectories to group template files by the application they are associated with.)

Create a new file at mycompany/templates/press/detail.html and add these lines:

<html>
<head>
<title>{{ press.title }}</title>
</head>
<body>
<h1>{{ press.title }}</h1>
<p>
Author: {{ press.author }}<br/>
Date: {{ press.pub_date }}<br/>
</p>
<p>
{{ press.body }}
</p></body>
</html>

This simple template file has placeholders for our title, author, pub_date, and body fields. When the template is rendered, these placeholders will be replaced with their respective values.

Now that we have a template, we can tell the view to use it.

Adding the template to the view

In our mycompany/press/views.py file, let's add a few lines to load our template. Replace the contents of your file with these lines:

from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.template import loader, Context
from mycompany.press.models import PressRelease

def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=1)
t = loader.get_template('press/detail.html')
c = Context({'press': p})
rendered_template = t.render(c)
return HttpResponse(rendered_template)

In the function, we're retrieving the press/detail.html template file and creating a special data object called Context. So for now, just understand that it passes data to the template so that it can be rendered. The context object in this example passes our press release object to the template in a variable called press.

Our template gets rendered into a string called rendered_template that is sent back to the browser via HttpResponse the same way we sent back simple lines of text in previous examples.

The rendered_template variable was used for clarity. You can omit it and just return the response like this:

def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=1)
t = loader.get_template('press/detail.html')
c = Context({'press': p})
return HttpResponse(t.render(c))

Point your browser to the URL http://localhost:8000/detail/1/. You should see something like this depending on what you entered earlier into the admin site as sample data:

Views, URLs, and Generic Views in Django 1.0

Creating the list view and template

In addition to displaying the detail for a specific press release, we'll also need a way to display a list of press releases. The steps to add this will be very similar to what we just did to add our detail view.

In your mycompany/press/views.py file, add the highlighted lines:

from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import get_list_or_404
from django.template import loader, Context
from mycompany.press.models import PressRelease

def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=1)
t = loader.get_template('press/detail.html')
c = Context({'press': p})
return HttpResponse(t.render(c))

def press_list(request):
'''
Returns a list of press releases
'''
pl = get_list_or_404(PressRelease)
t = loader.get_template('press/list.html')
c = Context({'press_list': pl})
return HttpResponse(t.render(c))

In your mycompany/press/urls.py file, add the highlighted line:

from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'detail/(?P<pid>d+)/$','mycompany.press.views.detail'),
(r'list/$', 'mycompany.press.views.press_list'),
)

Any incoming request starting with press/ will be sent to our press/urls.py file. If the remaining part of the URL is list/, it will be handled by the press_list function in our press/views.py file. If the remaining part is detail/<number> (such as detail/1 or detail/2), it will be handled by the detail function.

Finally, create a new file at mycompany/templates/press/list.html:

<html>
<head>
<title>Press Releases</title>
</head>
<body>
<h1>Press Releases</h1>
<ul>
{% for press in press_list %}
<li>
<a href="/press/detail/{{ press.id }}/">
{{ press.title }}</a>
</li>
{% endfor %}
</ul>
</body>
</html>

Point your browser to the URL http://localhost:8000/press/list/. You should see something like this, depending on what you entered earlier into the admin site:

Views, URLs, and Generic Views in Django 1.0

Using generic views to shorten development time

What we've done so far in this article is pretty standard for web application development:

  • We created a view to load an object by its ID.
  • We created a view to load a list of objects.
  • We retrieved our object using the data sent in from the URL or retrieved a list of objects.
  • We loaded a template file.
  • We rendered the template.
  • We returned an HTTP response.

Because these actions are so common, Django has a way to cut out the whole step of writing a view, called generic views. Generic views are called from the URL configuration file, which allows you to go right from the URL pattern to your template.

Generic views come in a few types:

  • Simple
  • List/detail
  • Date-based
  • Create/update/delete

We won't be covering the date-based or create/update/delete generic views. But after reading this article, you'll be well-prepared to read about them in the online documentation.

Simple generic views

The two simple generic views that handle loading of a template don't require any data lookup (going directly to a template) and redirecting from one URL to another.

Loading a template directly

If you just need to load and render a template when a URL is requested, you can use the direct_to_template generic view.

For example, let's build a robots exclusion file (aka a robots.txt file) that search engine spiders will request at http://localhost:8000/robots.txt. (Search engines wouldn't index pages on a localhost domain, but pretend for this example that they would.)

Since the file is rarely changed after being created, you may not want the overhead of a database lookup to serve it, so you just want to render a template when the URL is requested.

Create a new file at mycompany/templates/robots.txt and add these lines:

User-agent: *
Disallow: /admin

This very simple example will prevent spiders from trying to index your admin path (visit robotstxt.org for more info on how exclusion files work).

In your mycompany/urls.py file, add the highlighted lines:

from django.conf.urls.defaults import *
from django.contrib import admin

admin.autodiscover()

urlpatterns = patterns('',
(r'^admin/(.*)', admin.site.root),
(r'^press/', include('mycompany.press.urls')),
(r'^robots.txt$',
'django.views.generic.simple.direct_to_template',
'template': 'robots.txt'}),
)

Point your browser to the URL http://localhost:8000/robots.txt/. You'll get a response that looks like this:

Views, URLs, and Generic Views in Django 1.0

Django 1.0 Template Development A practical guide to Django template development with custom tags, filters, multiple templates, caching, and more
Published: December 2008
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

 

Redirecting URLs

If you want to automatically redirect one URL to another, you can use the redirect_to generic view.

For example, you might want to redirect from http://localhost:8000/press/ to http://localhost:8000/press/list/.

In your mycompany/press/urls.py file, add the highlighted line:


from django.conf.urls.defaults import *

urlpatterns = patterns('',
(r'detail/(?P<pid>d+)/$','mycompany.press.views.detail'),
(r'list/$', 'mycompany.press.views.press_list'),
(r'$', 'django.views.generic.simple.redirect_to',
{'url': '/press/list/'})
)

Point your browser to the URL http://localhost:8000/press/ and you will be redirected to http://localhost:8000/press/list/.

List/detail generic views

Generic views that handle object lists and object details can speed up your development time. Instead of writing views that do routine logic of retrieving object(s), loading and rendering templates, and then returning a response, we let the generic views do the heavy lifting for us.

Replacing the list view

Consider the press_list view we built earlier in the article at mycompany/press/views.py:

def press_list(request):
'''
Returns a list of press releases
'''
pl = get_list_or_404(PressRelease)
t = loader.get_template('press/list.html')
c = Context({'press_list': pl})
return HttpResponse(t.render(c))

We can completely replace the logic of this view by replacing our list configuration with a generic view.

Replace the highlighted lines in the mycompany/press/urls.py file:

from django.conf.urls.defaults import *
from mycompany.press.models import PressRelease

press_list_dict = {
'queryset': PressRelease.objects.all(),
}

urlpatterns = patterns('',
(r'detail/(?P<pid>d+)/$','mycompany.press.views.detail'),
(r'list/$',
'django.views.generic.list_detail.object_list',
press_list_dict),
(r'$', 'django.views.generic.simple.redirect_to',
{'url': '/press/list/'})
)

When you use generic views, some default assumptions will be made. For our example, we need to worry about these defaults:

  • The list of objects you send to the template in the context will be called object_list—this is a problem for us because our list of objects is called press_list.
  • It's OK to send an empty list to the template—currently, our view returns a 404 Error if the list is empty.
  • The template file will be called appname/modelname_list.html—currently, our template is called press/list.html.

We are faced with two choices: change our code or override these defaults. It would be easy to change our view and template name, but instead let's override the defaults by adding some extra keys to the dictionary we pass to the generic view.

Add the highlighted lines to the mycompany/press/urls.py file:

press_list_dict = {
'queryset': PressRelease.objects.all(),
'template_name': 'press/list.html',
'allow_empty': False,
'template_object_name': 'press',
}

By passing these overrides, we make our templates work as we created them.

You may be wondering about the value of the last key, template_object_name. Why is its value press when we are looking for an object named press_list in our template? Generic views add _list to the object name, so to get an object named press_list, we pass a template_object_name of press.

Replacing the detail view

Consider the detail view we created earlier in mycompany/press/views.py:

from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import get_list_or_404
from django.template import loader, Context
from mycompany.press.models import PressRelease

def detail(request, pid):
'''
Accepts a press release ID and returns the detail page
'''
p = get_object_or_404(PressRelease, id=1)
t = loader.get_template('press/detail.html')
c = Context({'press': p})
return HttpResponse(t.render(c))

Just as we did with the list view, we can replace the logic of this detail view by replacing our "detail" configuration with a generic view.

Add the highlighted lines to the mycompany/press/urls.py file:

from django.conf.urls.defaults import *
from mycompany.press.models import PressRelease

press_detail_dict={
'queryset': PressRelease.objects.all(),
'template_name': 'press/detail.html',
'template_object_name': 'press',
}

press_list_dict={
'queryset': PressRelease.objects.all(),
'template_name': 'press/list.html',
'allow_empty': False,
'template_object_name': 'press',
}

urlpatterns=patterns('',
(r'detail/(?P<object_id>d+)/$',
'django.views.generic.list_detail.object_detail',
press_detail_dict),
(r'list/$',
'django.views.generic.list_detail.object_list',
press_list_dict),
(r'$', 'django.views.generic.simple.redirect_to',
{'url': '/press/list/'})
)

A couple of things to note are:

  • We changed the name of the press release ID captured in the URL from pid to object_id. Generic views expect a variable called object_id and though there are ways to get around it, it's much simpler to change the name to what Django is expecting.
  • Looking at the press_detail_dict dictionary, it looks as if we are retrieving all of the press releases in our queryset, but the generic view is automatically going to use the object_id from our URL to filter the appropriate object.
  • There is no allow_empty key in the press_detail_dict. If the generic view can't find the object, it automatically returns a 404 error.

We have now completely replaced the functionality of the view functions in mycompany/press/views.py.

Using the other generic views

As mentioned, generic views come in two other varieties, date-based and create/update/delete. We won't be covering them, but here's a quick summary to whet your appetite for further exploration.

Date-based generic views allow you to create archives for your models that have a DateField or DateTimeField. You can set up different views for yearly, monthly, weekly, and daily displays of your content.

Create/update/delete generic views allow you to set up form-based pages to add new content, edit content, and delete content from your models. They are very similar to the add/edit pages from the admin application, and perform the same validation.

Comparing views and generic views

Now that we've seen both views and the generic views, you may be wondering why you would choose to use one over the other. The answer is usually found in the complexity of what you are trying to accomplish.

Don't let the simplicity of generic views fool you; entire sites have been written with only generic views. They can be much more complex than our simple examples were in this article, taking many additional arguments for specific functionality.

Try to use generic views when you can, and fall back to regular views when the complexity exceeds what you feel comfortable trying to do in a generic view. You may discover that what you want to do can be accomplished in a generic view, but that it's much simpler to do the same with a regular view.

Summary

In this article we learned how views and generic views can be used to display content. We explored how the URL dispatcher works and how it matches URLs to their associated view functions. We also learned how to pass data from the URL into our views using regular expressions. We built views to show a list of content and a content detail page, and then used generic views to reproduce that functionality.

Django 1.0 Template Development A practical guide to Django template development with custom tags, filters, multiple templates, caching, and more
Published: December 2008
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Scott Newman

Scott Newman has been developing commercial websites since 1997. Since then, he has professionally developed web applications in C, PERL, ColdFusion, ASP, PHP, and Python. He has also been a Windows network administrator and desktop application developer, but always gravitates back to web development. Scott holds a Network+ certification and is a dotMobi Certified Mobile Web Developer.

In recent years, Scott worked as the system development manager for a major media company developing CMS and mobile applications in Django. He is currently the consulting director for the Big Nerd Ranch in Atlanta, GA.

Books From Packt

Grails 1.1 Web Application Development
Grails 1.1 Web Application Development

PHP and script.aculo.us Web 2.0 Application Interfaces
PHP and script.aculo.us Web 2.0 Application Interfaces

Seam 2.x Web Development
Seam 2.x Web Development

Learning jQuery 1.3
Learning jQuery 1.3

jQuery UI 1.6: The User Interface Library for jQuery
jQuery UI 1.6: The User Interface Library for jQuery

Oracle SOA Suite Developer's Guide
Oracle SOA Suite Developer's Guide

Spring Web Flow 2 Web Development
Apache Struts 2 Web Application Development

Apache MyFaces Trinidad 1.2: A Practical Guide
Apache MyFaces Trinidad 1.2: A Practical Guide

Your rating: None Average: 5 (1 vote)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
M
u
p
y
5
A
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software