Chapter 5. URLs, HTTP Mechanisms, and Views

In the previous chapter, you learned how to define the data models which form the underpinnings of most Web applications; in the chapter following this one, we show you how to display those models with Django’s template language and forms. However, by themselves those two aspects of a Web framework don’t do much; you need controller logic deciding what data to render which template with and URL dispatching to determine what logic is performed for a given URL.

This chapter details how Django implements the HTTP request-response architecture introduced in Chapter 3, “Starting Out,” followed by an explanation of the simple Python functions that form controller logic, as well as some built-in helper functions that assist with common tasks.

URLs

The mechanisms for tying together a request’s URL and the resulting response are key to any Web development framework. Django uses a fairly simple, but powerful mechanism which enables you to map regular-expression-based configuration items to Python view methods, as well as link these lists of mappings together by including them within one another. Such a system is easy to use, but enables practically unlimited flexibility.

Introduction to URLconfs

The mappings mentioned previously are stored in Python files called URLconfs. These files must expose a urlpatterns object, which should be the result of a patterns function defined by Django. The patterns function, when called, consists of the following:

  • A leading prefix string, which can be blank

  • One or more Python tuples consisting of a regular expression (regex) string matching a URL or set of URLs; a view function object or string; and, optionally, a dict of arguments for that view function

Here’s an example to make sense of this, using an extended version of the URLconf from our blog application in Chapter 2, “Django for the Impatient: Building a Blog”:

from django.conf.urls.defaults import *

urlpatterns = patterns('myproject.myapp.views',
    (r'^$', 'index'),
    (r'^archives/(?P<year>d{4})/(?P<month>d{2})/(?P<day>d{2})/$', 'archive'),
)

Clearly, the regexes (first introduced in Chapter 1, “Practical Python for Django”) are the star of the show here. Aside from the lack of leading slashes (which are omitted due to their omnipresence in URLs), the primary thing to look for is the regex characters denoting the beginning and end of a string, ^ and $, respectively.

In practice, the ^ is nearly always required to remove ambiguity about what to match. The URL /foo/bar/ is not the same as /bar/, but the URL-matching regex r'bar/' would match both; r'^bar/' is more specific and matches only the latter.

The $ character is also used more often than not, for the same reason. It ensures the regex only matches the end of a URL and not the middle. However, $ is omitted for URL items designed to include other URLconf files because a URL leading into an include is not the final URL, but only part of one.

Note

Examine the first tuple in the previous example. You see it consists solely of r'^$'; that means it matches the root URL of the Web site, /. As mentioned previously, Django strips out the leading slash, and once that’s gone, we’re left with an empty string with nothing between the beginning (^) and the end ($). You use this a lot in your Django projects to define index or landing pages.

Another aspect of these regexes is they can use a part of regex syntax called symbolic groups (the parentheses-clad expressions beginning with ?P<identifier>) to capture parts of the URL that vary. This feature provides the power necessary to define dynamic URLs. In the previous example, we have an archives section of a blog, addressing individual entries based on their date. The fundamental information being accessed by the URL—a blog entry—does not change; only the date being accessed. As we see later, the values captured here are passed to the specified view function, which can use them in a database query or however else it sees fit.

Finally, once you’ve defined the regex, you simply need to note the function it’s linking to and possibly some extra keyword parameters (stored here as a dict). The first argument to patterns, if not empty, is prepended to the function string. Going back to the example for this section, you note the prefix string is 'myproject.myapp.views'; as a result, the full Python module paths go from 'index' and 'archive' to 'myproject.myapp.views.index' and 'myproject.myapp.views.archive', respectively.

Replacing Tuples with url

A relatively late addition to Django’s URL dispatching mechanism is the url method, which is designed to replace the tuples outlined previously while remaining nearly identical in structure. It takes the same three “arguments”—a regex, a view string/function, and optional argument dict—and adds another optional, named argument: name.name is simply a string, which should be unique among all your URLs; then, you can use it elsewhere to refer back to this specific URL.

Let’s rewrite the previous example using url.

from django.conf.urls.defaults import *

urlpatterns = patterns('myproject.myapp.views',
    url(r'^$', 'index', name='index'),
    url(r'^archives/(?P<year>d{4})/(?P<month>d{2})/(?P<day>d{2})/$', 'archive',
        name='archive'),
)

As you can see, it’s a simple drop-in replacement for the older tuple syntax. Because it’s an actual function and not just a tuple, it enforces what used to be merely a convention. The first two arguments are required and have no name, although the argument dict is now an optional named argument, kwargs, along with the new optional named argument, name.

Note

kwargs and name are named arguments instead of positional ones to support the fact they are both optional. You can specify neither of them, either of them, or both without running into problems. Positional arguments (or the use of tuples) would make such a setup far more difficult.

We’re presenting this url approach after the tuple-based syntax because it’s newer; even by the time you read this, there are still likely to be more Django URLconfs out in the wild utilizing tuples than using url. However, we strongly encourage you to use url in your own code; we have endeavored to set a good example by using it in the rest of this book because it offers more power and flexibility than the tuple approach.

Finally, see the example applications in Part III, “Django Applications by Example,” for more information on the name argument and how it can be used to reference back to your URLs from other parts of the code.

Using Multiple patterns Objects

One trick commonly used by Django developers is that of refactoring their URLconfs into multiple patterns calls per URL file, at least for files which have a nontrivial number of entries. This is possible because the return type of patterns is an internal Django object type that can be appended to as if it were a list or other container type. As such, it’s easy to concatenate multiple such objects together, and thus it’s possible and desirable to segregate them based on the prefix string. Here’s a semi-abstract example representing a top-level URL tying together multiple apps.

from django.conf.urls.defaults import *

urlpatterns = patterns('myproject.blog.views',
    url(r'^$', 'index'),
    url(r'^blog/new/$', 'new_post'),
    url(r'^blog/topics/(?P<topic_name>w+)/new/$', 'new_post'),
)

urlpatterns += patterns('myproject.guestbook.views',
    url(r'^guestbook/$', 'index'),
    url(r'^guestbook/add/$', 'new_entry'),
)

urlpatterns += patterns('myproject.catalog.views',
    url(r'^catalog/$', 'index'),
)

Note the use of the += operator for the second and third calls to patterns. By the end of the file, urlpatterns contains a conglomerate of all six defined URLs, each with their own distinct mappings thanks to the different prefix arguments. Of course, astute readers notice this still isn’t the height of refactoring. The “blog,” “guestbook” and “catalog” sections of the URL definitions are themselves slightly repetitive. Next, we cover how to streamline even further by including other URLconfs.

Including Other URL Files with include

The refactoring mindset seen in the previous section can be further applied, by breaking up URLconf files into multiple such files. This is most commonly seen in projects consisting of multiple apps, where there can be a “base” app defining an index page or other site-wide features such as authentication. The base application’s URLconf then defines the subsections filled in by other apps and uses a special include function to pass off further URL dispatching to said apps, as seen here in an update to the previous example.

## urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('myproject.blog.views',
    url(r'^$', 'index'),
    url(r'^blog/', include('myproject.blog.urls'),
)

urlpatterns += patterns('',
    url(r'^guestbook/', include('myproject.guestbook.urls')),
)

urlpatterns += patterns('',
    url(r'^catalog/', include('myproject.catalog.urls')),
)

## blog/urls.py

urlpatterns = patterns('myproject.blog.views',
    url(r'^new/$', 'new_post'),
    url(r'^topics/(?P<topic_name>w+)/new/$', 'new_post'),
)

## guestbook/urls.py

urlpatterns += patterns('myproject.guestbook.views',
    url(r'^$', 'index'),
    url(r'^add/$', 'new_entry'),
)

## catalog/urls.py

urlpatterns += patterns('myproject.catalog.views',
    url(r'^$', 'index'),
)

This example is actually a bit larger than the previous one, but hopefully you can see the benefits for a realistic Web site with dozens of potential URLs for each section; in each scenario, we save a decent amount of typing and repetition of the “blog,” “guestbook,” and “catalog” parts of the URL definitions. Specifically, we now have a multi-app Web site delegating most URLs to its subapplications, with the exception of the index page, which lives in the blog application (although you can make a base or similar application for such things—it’s entirely up to you).

URLconf including can be valuable even within single applications—there’s no hard limit on when to use multiple apps versus individual ones, so it’s entirely possible to have a Django app with hundreds of URLs. Most developers, in such a situation, would quickly start organizing things into modules, and URLconf including supports this as well. In general, the organization of your site is up to you, and the URLconf mechanisms have been set up to be as flexible as possible, in which includes play a large part.

Function Objects Versus Function-Name Strings

Throughout this section, we’ve been using strings to denote the Python module path to the view functions to which our URLs link. However, that’s not the only way to go about it; Django also enables you to pass in a callable object in place of a string, such as:

from django.conf.urls.defaults import *
from myproject.myapp import views

urlpatterns = patterns('', # Don't need the prefix anymore
    url(r'^$', views.index),
    url(r'^blog/', include('myproject.blog.urls')),
)

This opens up the door to a lot of functionality, such as using decorator functions to wrap generic views or even creating your own callable objects to do more complex delegation to different views. See Chapter 11, “Advanced Django Programming,” for more on decorators and other tricks usable with callable views.

Note

It’s sometimes tempting to do a blanket from myproject.myapp.views import * in your URLconf to use callable views, but this can lead to problems when mixing multiple view modules—imagine two separate view files each defining its own index view. Therefore it’s probably smart to follow the previous example and import each view module as its own object (using the from x import y as z style if necessary), resulting in a cleaner local namespace.

Modeling HTTP: Requests, Responses, and Middleware

You now understand how to set up your URL definitions and associate URLs with view functions; now it’s time to detail the ecosystem surrounding those view functions. As discussed in Chapter 3, Django models HTTP in a relatively simple request-response dichotomy with Python objects for requests and responses. Along with the URL dispatching and view functions, a request to your Web application flows such as:

  • HTTP request occurs to your Web server.

  • Web server passes request to Django, which makes a request object.

  • Django consults your URLconf to find the right view function.

  • That view function is called with the request object and any captured URL arguments.

  • The view then creates and returns a response object.

  • Django turns that response object into a format your Web server understands.

  • The Web server then responds to the requesting client.

We first go over the request and response objects and their components, and then get into Django middleware, which provides “hooks” into various stages of the previous process. Afterward, the next major section teaches you what you need to know about the views themselves.

Request Objects

Once you have your URLconfs set up, you need to define what behavior the URLs exhibit. We go over the details of view methods in a short while, but for now we show you the layout of the HTTP request and response objects, which those views are dealing with. All view functions accept a “request” parameter, which is an HttpRequest object, a nicely packaged set of attributes representing the raw HTTP request handed down from the Web server.

GET and POST Dictionaries

The most commonly accessed pieces of request-related data Web developers use are the GET and POST data structures, which are attributes of the HttpRequest object (request.GET and request.POST, as you can expect) and are represented as Python dictionaries. Although identical in structure, they are populated in two different ways, the importance of which is more than you can expect at first. Together, they offer a flexible way to parameterize Web requests.

Note

Although closely related to Python’s dict builtin, HttpRequest’s GET and POST attributes are actually instances of a Django-specific dict subclass, QueryDict, which is designed to mimic the underlying behavior of these data structures in the HTTP CGI specification. All key-value pairs store the value as a list, even in the case of a single value per key to correctly handle the cases where the HTTP server does return multiple values. Typical use of QueryDicts as dictionaries returns single items for convenience; you can use QueryDict methods, such as getlist, when you’re interested in multiple values.

GET parameters are passed as part of the URL string, but are not technically part of the URL itself in that they do not define a different resource (or view) but only change the behavior of the resource they’re attached to. For example, the URL /userlist/ can point to a page that lists the users of a community Web site; if the developer wants to break the list so it isn’t gigantic, he can decide to note the page number as a GET variable: /userlist/?page=2. The view being accessed is still the same, but the developer can look for a page key/value pair in the GET dictionary and return the correct page, such as in this abstract example.

def userlist(request):
    return paginated_userlist_page(page=request.GET['page'])

Note with request.GET, as with the other dict-like attributes of the request object, it’s helpful to make use of dictionary methods such as get (see Chapter 1Practical Python for Django,” for a refresher on dictionaries), so your logic doesn’t break down when the parameter you’re looking for isn’t specified.

POST parameters are not part of the URL but are effectively hidden from the user, often generated by an HTML form within a Web page. One of the attributes of the FORM tag, action, denotes which URL the data will be submitted to; if the user submits the form, the URL is called with a POST dict made up of the form fields. This is how most Web forms operate, although they can technically submit their data via the GET method as well. (This is not usually done, as it results in long, messy URLs for no good reason.)

In addition to GET and POST, HttpRequest objects expose a REQUEST dictionary, which searches both of the former in an attempt to return the requested key. This can be convenient in situations where a given key/value pair can be sent to your view via either method, and you’re unsure which was used; however, due to the Pythonic philosophy of “explicit is better than implicit,” most experienced Django programmers tend not to use this feature.

Cookies and Sessions

Following GET and POST, the next most commonly used aspect of request objects is request.COOKIES, which is yet another dictionary whose key/value pairs expose the HTTP cookies stored in the request. Cookies are a simple method by which Web pages can store persistent pieces of information in a user’s browser—they power most authentication systems on the Web and are used by some commercial sites to track a user’s surfing history.

Most cookies, generally, are used to enable a feature called sessions. This means that a Web page can ask the browser for a value identifying the user (which is set when the user first connects to the site or when the user logs in), and then uses this information to customize the behavior of the page for that user. Because cookies are easily manipulated on the client side, thus making it unsafe to store any real data in them, most Web sites store information in a server-side session object (usually via the site’s database) and leave only a unique session ID in the cookie itself.

Sessions are often used to implement state, as the HTTP protocol is by nature stateless—each request/response cycle stands by itself and has no knowledge of previous requests nor ways to pass information to later ones. With sessions, Web applications can work around this, storing items of data—such as messages to the user about whether submitting a form successfully saved her changes—on the server and sending them to the user in subsequent responses.

In Django, sessions are presented as yet another dictionary-like attribute of the HttpRequest object, request.session (note the session is lowercase, unlike the others—because sessions are not actually part of the HTTP protocol). Like the COOKIES attribute before it, session can be both read from and written to by one’s Python code. When first presented to your code, it contains the session as read from the database based on the user’s session cookie. If written to, it saves your changes back to the database, so they can be read later.

Other Server Variables

The previous aspects of the request object are the most often used; however, requests contain a host of other, usually read-only variables, some of which are part of the HTTP specification and others which are convenience attributes specific to Django. The following are all direct attributes of the request object:

  • path: The portion of the URL after the domain, for example, /blog/2007/11/04/; this is also the string that is handled by the URLconf.

  • method: One of two strings, 'GET' or 'POST', specifying which HTTP request method was used in this request.

  • encoding: A string specifying the encoding character set used to decode any form submission data.

  • FILES: A dict-like object containing any files uploaded via a file input form field, each represented as another dictionary with key/value pairs for the filename, the content type, and the file content itself.

  • META: Another dictionary, containing the HTTP server/request variables not handled by the other aspects of the request, including CONTENT_LENGTH, HTTP_REFERER, REMOTE_ADDR, SERVER_NAME, and so forth.

  • user: The Django authentication user, which only appears if your site has Django’s authentication mechanisms activated.

  • raw_post_data: The raw, unfiltered version of the POST data contained within this request. The use of request.POST is almost always preferable over accessing request.raw_post_data, but it’s here for examination by those with advanced needs.

Response Objects

At this point, you’ve read about the information that is passed into our view function; now we examine what it’s responsible for returning, namely a response. From our point of view, responses are simpler than requests—their primary data point is the body text, stored in the content attribute. It’s usually a large string of HTML, and it’s so central to HttpResponse objects there are a couple of ways of setting it.

The most common method is via the act of creating the response object—HttpResponse takes a string as a constructor argument, which is then stored in content.

response = HttpResponse("<html>This is a tiny Web page!</html>")

Just with that, you’ve got a fully functioning response object, one worthy of being returned farther up the stack to the Web server and forwarded to the user’s browser. However, it’s sometimes useful to build the response content piece-by-piece; to support this, HttpResponse objects implement a partial file-like behavior, notably the write method.

response = HttpResponse()
response.write("<html>")
response.write("This is a tiny Web page!")
response.write("</html>")

Of course, this means you can use an HttpResponse for any code that expects a file-like object—for example, the csv module’s CSV writing utilities—that adds a lot of flexibility to the process of generating the information your code returns to end users.

Another key aspect of response objects is the capability to set HTTP headers by treating the HttpResponse object like a dictionary.

response = HttpResponse()
response["Content-Type"] = "text/csv"
response["Content-Length"] = 256

Finally, Django provides a number of HttpRequest subclasses for many common response types, such as HttpResponseForbidden (which uses the HTTP 403 status code) and HttpResponseServerError (similar but for HTTP 500 or internal server errors).

Middleware

Although the basic flow of a Django application is fairly simple—take in request, find appropriate view function, return a response—extra layers are available that can be leveraged to add a lot of power and flexibility. One of these extra layers is middleware—Python functions executed at various points in the previous process that can alter the effective input (by modifying the request before it reaches the view) or output (modifying the response created by the view) of the entire application.

A middleware component in Django is simply a Python class which implements a certain interface, namely it defines one of a number of methods with names such as process_request or process_view. (We examine the most commonly used ones in the following subsections.) When listed in the MIDDLEWARE_CLASSES tuple in your settings.py file, Django introspects the middleware class and calls its method at the appropriate time. The order of classes listed in your settings file determines the order in which they are executed.

Django comes with a handful of built-in middleware, some of which are generally useful and others which are required for specific “contrib” applications such as the authentication framework. See the official Django documentation for more on these.

Request Middleware

On the input side sits request middleware, which is defined as a class that implements the process_request method, as in the following example:

from some_exterior_auth_lib import get_user

class ExteriorAuthMiddleware(object):
    def process_request(self, request):
        token = request.COOKIES.get('auth_token')
        if token is None and not request.path.startswith('/login'):
            return HttpResponseRedirect('/login/')
        request.exterior_user = get_user(token)

Note the line assigning a value to request.exterior_user, which illustrates a common use of request middleware: adding extra attributes to the request object. In the situation where that line is called, process_request implicitly returns None (Python functions always return None if they lack an explicit return statement), and in that case Django continues to process other request middleware and eventually the view function itself.

If, however, the test checking for a valid auth token (and making sure the user isn’t currently trying to log in!) fails, our middleware redirects the user to the login page. This illustrates the other possible behavior of middleware methods; they can return an HttpResponse (or subclass) that is immediately sent off to the requesting client. In this case, because our middleware is a request middleware, everything past that point in the normal flow of things—including the view that would have been called—is skipped.

Response Middleware

As you can expect, response middleware is run on the HttpResponse objects returned by view functions. Such middleware must implement the process_response method, which accepts request and response parameters and returns an HttpResponse or subclass. Those are the only limitations—your middleware can modify the response it is given or create an entirely new response and return that instead.

One of the most common uses of response middleware is to inject extra headers into the response, either across the board—such as enabling caching-related HTTP features—or conditionally, such as a built-in middleware that sets Content-Language equal to the current translation.

Following is a trivial example that does a simple search and replace of “foo” with “bar” on all text output by the Web application:

class TextFilterMiddleware(object):
    def process_response(self, request, response):
        response.content = response.content.replace('foo', 'bar')

We could have made this a more realistic example that filters out naughty words (which can be useful for a community Web site, for example), but this is a family book!

Views/Logic

Views (née controllers) form the core of any Django Web application in that they provide nearly all the actual programming logic. When defining and using the models, we’re database administrators; when writing the templates, we’re interface designers; but when writing views, we’re truly software engineers.

Although the views themselves can easily account for a large portion of your source code, the Django framework code surrounding views is surprisingly slim. Views represent your business logic and are thus the aspect of a Web application which needs the least glue code and the most custom work. At the same time, built-in generic views are one of the most touted time-savers in Web development frameworks such as Django, and we introduce these and methods of using them both on their own and in tandem with custom views.

Just Python Functions

At the heart of it, Django views are Python functions, plain and simple. The only restriction on view functions is they must take an HttpRequest object and return an HttpResponse object, which is described previously. Also previously mentioned are the regex patterns from the URLconfs, in which you can define named groups. Combined with an optional dictionary parameter, they provide the arguments to the view function, as in the following example (slightly altered from its earlier incarnation):

urlpatterns = patterns('myproject.myapp.views',
    url(r'^archives/(?P<year>d{4})/(?P<month>d{2})/(?P<day>d{2})/$', 'archive',
        {'show_private': True}),
)

Combined with the HttpRequest object, the archive view function referenced by the previous URL could have a signature such as:

from django.http import HttpResponse

def archive(request, year, month, day, show_private):
    return HttpResponse()

As long as it returns an HttpResponse of some kind, the inner guts of the method are inconsequential; what we’re seeing here is essentially an API. As with any API, you can use prewritten code implementing it or write your own from scratch. We examine your options in that order.

Generic Views

Possibly the most-touted aspect of Django, and Web frameworks in general, is the capability to use predefined code for the so-called CRUD operations that make up most of the average Web application. CRUD stands for Create, Read (or Retrieve), Update, and Delete, the most common actions taken in a database-backed application. Showing a list of items or a detail page for a single object? That’s Retrieve. Displaying an edit form and altering the database when it’s submitted? That’s Update or Create, depending on your application and the form in question. Delete should need no explanation.

These tasks and their variants are all provided for by Django’s set of generic views. As previously shown, they are simply Python functions, but ones that are highly abstracted and parameterized to achieve maximum flexibility in their defined role. Because they handle the logic, framework users simply need to refer to them in their URLconf files, pass the appropriate parameters, and make sure a template exists for the view to render and return.

For example, the object_detail generic view is intended to facilitate display of a single object and takes its parameters from both the URL regex and the argument dictionary to do so:

from django.views.generic.list_detail import object_detail
from django.conf.urls.defaults import *

from myproject.myapp.models import Person

urlpatterns = patterns('',
    url(r'^people/(?P<object_id>d+)/$', object_detail, {
        'queryset': Person.objects.all()
    })
)

In the previous example, we’ve defined a regex that matches URLs such as /people/25/, where 25 is the database ID of the Person record we want to display. The object_detail generic view needs both an object_id argument and a QuerySet it can filter to find the object identified by that ID. In this case, we provide object_id via the URL and the queryset via the argument dictionary.

Generic views often expose a handful of options. Some are specific to that view; although others are global, such as a template_name argument enabling the user to override the default location of the view’s template or an extra_context dict which enables the user to pass extra information into the template’s context. (See Chapter 6, “Templates and Form Processing,” for more on templates and contexts.) You can see the official Django documentation for details on all the generic views and their arguments; we go over some of the more commonly used ones next. Note generic views are organized in a two-level module hierarchy for neatness’ sake.

  • simple.direct_to_template: Useful for templates that have some dynamic content (as opposed to flatpages, which are static HTML, see Chapter 8, “Content Management System”) but require no specific Python-level logic, such as index pages or nonpaginated/mixed list pages.

  • list_detail.object_list and list_detail.object_detail: These two provide the primary read-only aspect of most Web apps and are probably the most commonly used generic views, as information display doesn’t usually require complex logic. However, if you need to perform logic to prepare your template context, you can find yourself wanting custom views instead.

  • create_update.create_object and create_update.update_object: Useful for simple object creation or update, where all you need is the form validation defined in your form or model (see Chapters 6 and 4, respectively) and where no other business logic applies.

  • date_based.*: A handful of date-based generic views which highlight Django’s origin as a publication-oriented framework. They are extremely useful for any date-based data types. Included are date-oriented index and detail pages plus sublist pages ranging from the year down to the day.

Generic views are both a blessing and a curse. The blessing aspect should be obvious; they save a lot of time and can be used to cut out almost all the work involved in simple or moderately complex views. Their usefulness is further expanded by wrapping them within custom views, as we outline next. However, generic views’ usefulness can make it difficult to accept that sometimes; you just have to write your own completely custom view from scratch, even if the generic view closest to your vision would get you 90 percent of the way there. Knowing when to throw in the towel and go the custom route is a valuable skill, which, like many aspects of software development, can only truly be picked up with experience.

Semi-generic Views

There are times when generic views on their own, called straight from a URLconf file, do not suffice. Often, this requires a completely custom view function to be written, but equally often, a generic view can still be leveraged to do the grunt work depending on the logic required.

The most common use of such “semi-generic” views, in our experience, has been to work around an inherent limitation in the URLconf itself. You can’t perform logic with the captured URL parameters until the regex has been parsed. This limitation exists due to the way URLconfs are designed, and it’s easy to work around it. Consider the following snippet combining portions of a URLconf file and a view file:

## urls.py

from django.conf.urls.defaults import *

urlpatterns = patterns('myproject.myapp.views',
    url(r'^people/by_lastname/(?P<last_name>w+)/$', 'last_name_search'),
)

## views.py

from django.views.generic.list_detail import object_list
from myproject.myapp.models import Person

def last_name_search(request, last_name):
    return object_list(request,
        queryset=Person.objects.filter(last__istartswith=last_name)
    )

As you can see, although our function takes the last_name argument defined as a named group in the URL regex, we’re still delegating 99 percent of the actual work to the generic view. This is possible because generic views are normal Python functions and can be imported and called as such. It’s easy to fall into the trap of thinking about the framework as its own language, but as we’ve emphasized before, it’s all just Python, and this sort of trick shows why that’s a good thing.

Custom Views

Finally, as mentioned earlier, it’s sometimes the case you can’t use generic views at all, which brings us back to the beginning of this section; the view functions as a blank slate, conforming only to a simple API, waiting for you, the programmer, to fill it however you want. We share a couple of observations based on our own experience and point out some convenient shortcut functions supplied by the framework; however, in general, this is an area in which your own skills and experiences determine what you do next.

Framework-Provided Shortcuts

As we’ve stated, once you’re in the realm of custom views, Django basically leaves you alone. However, it does provide a handful of shortcuts, most of which are defined in the django.shortcuts module.

  • render_to_response: A function that replaces the two- or three-step process of creating a Context object, rendering a Template with it, and then returning an HttpResponse containing the result. It takes the template name, an optional context (Context object, or dictionary, as usual) and/or MIMEtype, and returns an HttpResponse object. Template rendering is covered in Chapter 6.

  • Http404: An Exception subclass which effectively returns an HTTP 404 error code and renders a top-level 404.html template (unless you’ve overridden this in your settings.py). To use it, you raise it like you would any other exception, the idea being that when you encounter a 404 condition, it’s a full-fledged error, same as if you had tried to add a string to an integer. It is defined in the django.http module.

  • get_object_or_404 and get_list_or_404: These two functions are simple shortcuts for obtaining an object or list or raising Http404 if the lookup fails. They take a klass argument—which is flexible enough to take a model class, a Manager, or a QuerySet—and some database query arguments such as those passed to Managers and QuerySets and attempt to return the object or list in question.

Here are two examples using the previous shortcuts: The first uses Http404 by itself, and the second shows how to streamline things using get_object_or_404—the two functions exhibit identical behavior in practice. Don’t worry about the template paths for now; those are explained in more detail in Chapter 6.

Here’s the manual method of raising a 404 exception:

from django.shortcuts import render_to_response
from django.http import Http404
from myproject.myapp.models import Person

def person_detail(request, id):
    try:
        person = Person.objects.get(pk=id)
    except Person.DoesNotExist:
        raise Http404

    return render_to_response("person/detail.html", {"person": person})

And an example of get_object_or_404, which you’ll usually want to use in place of the preceding method:

from django.shortcuts import render_to_response, get_object_or_404
from myproject.myapp.models import Person

def person_detail(request, id):
    person = get_object_or_404(Person, pk=id)

    return render_to_response("person/detail.html", {"person": person})

Other Observations

Perhaps a minor point—many Django developers find themselves making use of the “args/kwargs” convention when defining their own view functions. As seen in Chapter 1, Python functions can define *args and **kwargs to accept arbitrary positional and keyword arguments; although a two-edged sword (concrete function signatures are often a source of excellent documentation, but are lost here), this is often a useful trick to increase flexibility and is also faster to boot. You no longer have to move back to your URLconf file to remember exactly what you named your captured regex parameters or keyword arguments, just define your function as

def myview(*args, **kwargs):
    # Here we can refer to e.g. args[0] or kwargs['object_id']

and away you go, referring to kwargs["identifier"] when necessary. After a while, doing this becomes second nature, and it also makes things easier when you want to pass on a function’s arguments to a delegate function—such as in the “semi-generic” views mentioned previously.

Summary

We’re now more than halfway done exploring the basics of Django’s core components. In addition to the models described in Chapter 4, this chapter has shown you how Django implements URL dispatching and the rest of the HTTP request-response “conversation,” including the use of middleware. You’ve also seen how to put together simple Django view functions, and you’ve gotten a taste of the included generic views and how they can be utilized.

The next and last chapter in this section of the book, Chapter 6, describes the third major piece of the puzzle, that of rendering Web pages via templates and managing input from users with forms and form validation. Afterward, it’s on to Part III where you get to see these concepts put to use in four example applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.190.18