CHAPTER 4
URLs and Views

Much of this book is split into fairly self-contained chapters, but this one covers two seemingly unrelated concepts together, because each relies very much on the other. URLs are the primary entry points to your site, while views are the code that respond to incoming events. What goes on in a view is very open-ended. Aside from accepting a request and returning a response, there's no particular protocol that views should adhere to, and no rules about what they are or aren't allowed to do.

The possibilities for views are too vast to consider describing in detail, and there aren't any utilities designed explicitly for views to use while executing. Instead, it's possible to hook into the process Django uses to map Web addresses to the views they should execute. This makes the link between URLs and views extremely important, and a thorough understanding of it can enable further advanced techniques.

Also, in terms of how Django manages incoming requests, URL configurations exist solely to dispatch a request to a view that can handle it. Discussing URLs and URL configurations independently of views would be of little value.

URLs

Since all incoming requests to a Web server originate with the Web browser accessing a URL, a discussion of URLs is an important place to start. The process taken by the browser to transform a URL into a message to be sent to the Web server is beyond the scope of this chapter, but Chapter 7 provides more information.

One common point of confusion is whether a Web address should be called a Uniform Resource Identifier (URI) or a Uniform Resource Locator (URL). Many people use these two terms interchangeably, regardless of whether they know the difference. In a nutshell, a URI is a complete addressing mechanism that includes two pieces of information.

  • The name of the scheme or protocol to be used to connect to the resource. This is always followed by a single colon.
  • The path where the resource can be found. The exact format of this path may be different for different schemes, so not all URI paths look alike.

URLs, on the other hand, are addresses from a small set of connection schemes whose path portions all conform to a single format. Included in this set are such common protocols as HTTP, HTTPS and FTP—essentially the common protocols found on the Web today. The path format shared by these protocols is as follows:

  • The protocol to be used to access the resource, such as http:// for standard HTTP. This is a slight extension to the scheme portion of the URI because it is assumed that all URL protocols will include two forward slashes following the colon.
  • The host domain where the resource can be found, such as prodjango.com.
  • Optionally, the port number the server responds to. Each protocol has a default port that will be used if one isn't supplied. For standard HTTP, this is 80, while for encrypted HTTP using the Secure Sockets Layer (SSL), it will be 443.
  • The path of the resource on the server, such as /chapter4/.

So while all URLs are certainly URIs, not all URIs are URLs. That subtle distinction can be confusing when working on the Web because either term can be used to describe the addresses found everywhere. Since Django is built for the Web—and thus the addresses covered under URL schemes—the rest of this book will refer to these addresses as URLs, as the full range of URIs might not be suitable for Django's dispatching mechanism.

Standard URL Configuration

Django doesn't provide any features for automatically discovering or generating a URL structure for any site. Instead, each site and application is expected to explicitly declare whatever addressing scheme is most appropriate using URL configurations. This isn't a limitation—it's a feature that allows you to define your site's addresses the way you'd like. After all, sites on the Web are like real estate; your Web framework shouldn't determine your floor plan.

Defining a URL configuration may seem quite simple, but there's a bit going on that merits some special attention, especially since Django's own tools aren't the only way to define this configuration. The implementation lives at django.conf.urls.defaults and provides two functions that work together to manage URL configurations.

The patterns() Function

A URL configuration consists of a list of patterns that each map a particular type of URL to a view. These patterns each have a few components but all of them are specified together as arguments to the patterns() function.

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^$','post_list'),
    (r'^(?P<id>d+)/$', 'post_detail'),
    (r'^(?P<id>d+)/comment/$','post_comment'),
)

The arguments for this function can be placed in two groups:

  • A single import path prefix for any views that are specified as strings
  • Any number of URL patterns

Historically, all views were specified as strings, so the prefix was a great way to reduce the amount of duplication required to map URLs to views from a single application. More recently, URL patterns are allowed to specify views as callables, in which case the prefix would be ignored. It is still often useful to specify views as strings using a prefix, as it reduces the overall code by not requiring a set of imports for the views.

The URL patterns are traditionally passed in as tuples, though "The url() Function" section describes a more recent addition. Details of each portion of this tuple are as follows:

  • A regular expression used to match against the URL
  • The view function to be called for a request matching this pattern
  • Optionally, a dictionary of arguments to be passed to the function

This tuple contains all the information necessary to map an incoming request to a view function. The URL's path will be checked against the regular expression, and if a match is found, the request is passed along to the specified view. Any arguments captured by the regular expression are combined with the explicit arguments in the extra dictionary, then passed along to the view along with the request object.

The url() Function

In an effort to provide better flexibility in the long run, URL pattern tuples have been deprecated in favor of the url() utility function. url() takes the same arguments that are passed into the tuple, but can also take an extra keyword argument to specify the name of the URL pattern being described.

This way, a site can use the same view multiple times, yet still be able to be referenced using reverse URL lookups. More information on that can be found later in this section.

The include() Function

Rather than supplying all your URL patterns in a single file, the include() function allows them to be split up among multiple files. It takes a single argument: an import path where another URL configuration module can be found. This not only allows the URL configuration to be split across multiple files, but it also allows the regular expression to be used as a prefix for the included URL patterns.

One important thing to remember when using include is to not specify the end of the string in the regular expression. The expression should never end in a dollar sign ($). The dollar sign ($) causes the expression to only match the full URL. This wouldn't leave any additional URL fragments to pass along to the included configuration. This means that the extra URL patterns would only be matched if they check specifically for an empty string.

Resolving URLs to Views

Views are rarely called directly by your own code but are instead invoked by Django's URL dispatch mechanism. This allows views to be decoupled from the particular URLs that trigger them, and the details of how those two aspects are linked can be safely ignored for most projects. But since views don't always have to just be simple functions, knowing how Django goes from URL to view is important in order to determine what views are truly capable of.

Mapping URLs to views is a simple, well-documented process, but it's worth covering the basics here for reference. A typical URL pattern consists of a few distinct items:

  • A regular expression to match against the incoming URL being requested
  • A reference to the view to be called
  • A dictionary of arguments to be passed along every time the view is accessed
  • A name to be used to reference the view during reverse lookups

Since URL patterns are expressed in regular expressions, which can capture certain portions of a string for later use, Django uses this as a natural way to pull arguments out of a URL so they can be passed to a view. There are two ways these groups can be specified, which determine how their captured values are passed into the view.

If groups are specified without names, they're pulled into a tuple, which is expanded into excess positional arguments. This approach makes the regular expression a bit smaller, but it has some drawbacks. Not only does it make the regular expression a bit less readable, it also means that the order of arguments in your view must always match the order of the groups in the URL, because Django sends them in as positional arguments. This couples the URL to the view more than is usually preferable; in some situations, such as the object-based views described later in this chapter, it can still be quite useful.

If groups are given names, Django will create a dictionary mapping those names to the values that were extracted from the URL. This alternative helps encourage looser coupling between URLs and views by passing captured values to the view as keyword arguments. Note that Django doesn't allow named and unnamed groups to be used together in the same pattern.

Resolving Views to URLs

As alluded to in the previous section, there's another URL resolution process that Django provides, which can be of even more use if applied properly. Applications often need to provide links or redirects to other parts of the application or elsewhere on the site, but it's not usually a good idea to hard-code those links directly. After all, even proprietary applications can change their URL structure, and distributed applications may not have any idea what the URL structure looks like in the first place.

In these situations, it's important to keep the URLs out of the code. Django offers three distinct ways to specify a location without needing to know its URL in advance. Essentially, these all work the same way, as they all use the same internal machinery, but each interface is suited for a particular purpose.

The permalink Decorator

One of the most obvious places for code to reference a URL is in the get_absolute_url method of most models. Providing this method is a common convention, so templates can easily provide a direct link to an object's detail page without having to know or care what URL or view is used to display that page. It doesn't take any arguments and returns a string containing the URL to be used.

To accommodate this situation, Django provides a decorator, living at django.db.models.permalink, which allows a function to return a set of values describing a view to be called, transforming it into a URL that calls the view. These values are provided as the return value from a function such as the get_absolute_url method and follow a specific structure—a tuple containing up to three values.

  • The first value is the name of the view to be called. If the view was named, that name should be used here. If not, the import path of the view should be used instead. This is always required.
  • The second value is a tuple of positional arguments that should be applied to the view.If there are no arguments to be applied to the view at all, this value doesn't need to be provided, but if keywords are needed, this should be an empty tuple.
  • The third value in this tuple is a dictionary mapping keyword arguments to their values, all of which will be passed to the specified view. If no keyword arguments are necessary, this value can be left out of the tuple.

Given the following URL configuration:

from django.conf.urls.defaults import *
from library.import models

urlpatterns = patterns('django.views.generic',
    url(r'^articles/(?P<object_id>d+)/$', 'list_detail.object_detail', {
        'queryset': models.Article.objects.all(),
    }, name='library_article_detail'),
)

a corresponding model (located in a library application) might look like this:

from django.db import models

class Article(models.Model):
    title = models.CharField(max_length=255)
    slug = models.SlugField()
    pub_date = models.DateTimeField()

    def get_absolute_url(self):
        return ('library_article_detail',
            (), {'object_id': self.id})
    get_absolute_url = mod0els.permalink(get_absolute_url)
The url Template Tag

Another common need is to have templates provide links to views that aren't based on models but still shouldn't have a hard-coded URL. For instance, a link to a contact form doesn't necessarily have any ties to the database or any models, but will still need to be linked to in a way that can accommodate future changes or distribution.

The syntax for this template looks quite similar to the permalink decorator because it passes values to the same utility function. There are some slight differences, because as a template tag, it doesn't use true Python code.

{% url library_article_detail object_id=article.id %}
The reverse() Utility Function

Django also provides a Python function that provides the translation from a description of a view and its arguments to a URL that will trigger the specified view. Living at django. core.urlresolvers, the reverse() function does exactly that. It takes all the same arguments described for the previous two techniques, but also one other, allowing it to specify which URL configuration module should be used to resolve the URL. This function is used internally by both the permalink decorator and the url template tag. The reverse() function takes up to four arguments.

  • viewname—The name of the view to be called or the import path if no name was specified. This is always required.
  • urlconf—The import path of a URL configuration module to use for lookups. This is optional and if it's absent or None, the value is taken from the ROOT_URLCONF setting.
  • args—A tuple of any positional arguments that will be passed to the view.
  • kwargs—A dictionary of any keyword arguments that will be passed to the view.

Using the same example as in the previous section, here's how reverse() would be used to obtain a URL for a specific object.

>>> from django.core.urlresolvers import reverse
>>> reverse('library_article_detail', kwargs={'object_id': 1})
'/articles/1/'

Keep in mind that args and kwargs are separate, distinct arguments. The reverse utility function does not use any form of the argument expansion described in Chapter 2.

Views

One point of confusion for programmers coming from other environments is the fact that Django uses the term "view" a bit differently than others. Traditionally, the view in a Model-View-Controller (MVC) architecture refers to the display of information to a user—essentially, the output portion of a user interface.

The Web doesn't work like that. Viewing data is typically a direct result of a user action, and updates to that view only take place as responses to subsequent actions. This means that the output process is irrevocably linked to the user input process, which can cause some confusion about how even the traditional MVC pattern should define a view. So there is no simple answer to the question of how Django's views compare to those of other environments because there isn't anything solid to compare against. People from different backgrounds are likely to have different expectations about what a view should be. The bad news is that Django probably doesn't line up with any of them. The good news is that once you start working with Django, the notion of a view is clearly defined, so there's little confusion when communicating with other Django developers.

Templates Break It Up a Bit

Django's views do perform the basic function of the output interface, because they're responsible for the response that is sent to the browser. In a strict sense, this response is the entire output, and it contains all the information about what the user will see. This is often too much work to do in Python while still making it readable, so most views rely on templates to generate the bulk of the content.

The most common practice is to have each view call a single template, which may make use of a number of tools to minimize the amount of template code that must be written for use by a particular view. Chapter 6 includes further details on the template language and the tools that can be used, but the important thing to know for this section is that templates are a great way to simplify the coding process as a whole. They help cut down on the amount of code that must be written, while simultaneously making that code more readable and maintainable for the future.

While Chapter 1 listed templates as a separate layer, remember that they're really just a tool that Django makes available to other parts of an application, including views. Ultimately, whether or not templates are used to generate content, the view alone is responsible for generating the final response. Django's template system has no concept of requests or responses; it just generates text. It's up to views to handle the rest.

Anatomy of a View

A view is a function that takes an HTTP request and returns an HTTP response. That is a bit simplistic, given the potential power of views, but that's really all there is to it. A view always receives, as its first argument, the HttpRequest created by Django, and it should always return an HttpResponse, unless something went wrong. Full details on those objects, their purpose and their properties are covered in Chapter 7.

The first aspect of that definition is the notion that a view must be a standard function. This definition is a bit flexible because in reality, any Python callable can be used as a view; it just happens that basic functions are easy to work with and provide everything that's necessary for most situations. Methods—both on classes and instances—and callable objects, using the protocol described in Chapter 2, are all perfectly valid for use as views. This opens up a variety of other possibilities, some of which will be described later in this chapter.

The next point is the one immutable when it comes to views. Whenever a view is called, regardless of what other arguments are passed along, the first argument is always an HttpRequest object. This also means that all views must accept at least this one object, even those views that don't have use for any explicit arguments. Some simple views, such as those that display the server's current time, may not even use the request object, but must always accept it anyway to fulfill the basic protocol of a view.

On the subject of arguments, another point is that a view must be able to accept whatever arguments are passed to it, including those captured from the URL and those passed into the site's URL configuration. This may seem obvious, but a common point of confusion is the presumption that Django uses some kind of magic to allow a URL configuration to specify which template should be used, without requiring any supporting code in the view.

Django's generic views all accept a separate argument to modify the template name, and many users assume that Django somehow passes this straight through to the template system to override what name the view uses by default. The truth is that the generic views have special handling for this argument, and the view itself is responsible for telling the template system which template to use. Django relies on standard Python, so there's no magic behind the scenes that tries to interpret what your arguments are supposed to mean. If you plan to supply an argument to a function, make sure that the view knows how to deal with it.

The last notion from that original description of views is that a view must return an HttpResponse object, and even that isn't entirely accurate. Returning a response is definitely the primary goal of all views, but in certain situations it's more appropriate to raise an exception, which will be handled in other ways.

What goes on between request and response is largely unrestricted, and views can be used for as many purposes as there are needs to be met. Views can be built to serve a specific purpose or they can be made generic enough to be used in distributed applications.

Writing Views to Be Generic

A common theme in Django development is to make code as reusable and configurable as possible so that applications and snippets are useful in more than one situation, without having to rewrite code for every need. That's the whole point of DRY: Don't Repeat Yourself.

Views present a bit of a challenge with regards to DRY, since they're only called by incoming requests. It may seem like it wouldn't be possible to write a view that could be called for anything other than the request it was originally intended for. Django itself, however, is full of examples of generic views, which can be used for a variety of applications and situations with only a small amount of configuration necessary for each new use.

There are a few guidelines that can greatly aid the reuse of views, making them generic enough to be used throughout a variety of applications. Views can even be made so generic that they can be distributed to others and included in projects the original author had no concept of.

Use Lots of Arguments

Typically, a view could perform quite a few different tasks, all combining to solve a particular problem. Each of these tasks often has to make assumptions about how it should work, but these assumptions can typically be pulled out into a configurable option using arguments. Consider the following view, designed to retrieve a blog post and pass it along to a template.

from django.shortcuts import render_to_response
from django.template import RequestContext

from blog.models import Post

def show_post(request, id):
    post = Post.objects.get(id=id)
    context = RequestContext(request, {'post': post})
    return render_to_response('blog/detail.html', context)

This view will work perfectly well for its intended purpose, but it's quite tightly connected to a specific blog application. It's still loosely coupled in the sense that it doesn't need to deal with the details of how to retrieve the blog post or render the template, but still relies on details specific to the blog application, such as the model and template.

Instead, it's possible to move these assumptions into arguments that can be swapped out for other situations. While initially this will involve some extra work, it can save a lot of time later, if this view is used in a great number of situations. More importantly, the more complex the view, the more code that can be reused using this technique. Once these options have been moved out into arguments, specific values can be passed in with a URL configuration, so a view doesn't have to be written for each purpose.

For this particular view, a few things can be factored out in this way. The model doesn't need to be known in advance and the view should also be able to work with a QuerySet so that a particular URL could operate on a limited set of data. Also, the field name shouldn't be hard-coded, and the template name should be provided outside the view.

from django.shortcuts ;import render_to_response
from django.template import RequestContext

def show_object(request, id, model, template_name):
    object = model._default_manager.get(pk=id)
    context = RequestContext(request, {'object': object)})
    return render_to_response(template_name, context)

Then, when it comes time to use this view, it's easy to customize by providing these details using a URL configuration. Simply supply the argument values as an extra dictionary in the URL configuration, and they'll be passed along each time the view is called from that URL pattern.

from django.conf.urls.defaults import *

from blog.models import Post

urlpatterns = patterns('',
    (r'^post/(?P<id>d+)/$', 'blog.views.show_object', {
        'model': Post,
        'template_name': 'blog/detail.html',
    }),
)

This approach can even be used with models that use other types of IDs, such as a music database using catalog numbers in the format of DJNG-001; anything that can be guaranteed unique among all objects can be used as an object's primary key. Since our new generic view simply passes the ID straight through to the database API, it's easy to support these other types of IDs by simply adjusting the URL pattern appropriately.

r'^album/(?P<id>[a-z]+-[0-9])/$'

This particular view shouldn't have to be written in the first place, because Django provides one out of the box for this purpose, object_detail, and it's even more versatile than the example shown here. It uses nearly a dozen different arguments, all of which are expected to be customized in URL configurations.

Once you have a view that accepts a number of arguments for customization, it can become quite easy to require far too many arguments be specified in each URL configuration. If every use of a view requires all the configuration options to be specified, it could quickly become just as much work to use the generic view as it would be to write the view from scratch each time. Clearly, there needs to be a better way to manage all these arguments.

Provide Sensible Defaults

Since functions can define default values for any arguments that can use them, the most reasonable way to manage this complexity is to provide decent defaults wherever possible. Exactly what defaults can be provided and what they look like will be different for each view, but it's usually possible to come up with some sensible values for them.

Sometimes you have a number of views that each serve a different purpose but may have some code in common. This is often boilerplate, which every view needs to use, but isn't geared toward the true functionality of any individual view.

For example, views for private pages must always verify that users are logged in and that they have the appropriate permissions. An application may have a dozen different types of views, but if they're all private, they must all use that same code every time. Thankfully, we're working in Python, which provides a useful alternative.

View Decorators

Most boilerplate in views is either at the very beginning or the very end. Usually it handles such tasks as initializing various objects, testing standard prerequisites, handling errors gracefully or customizing the response before it goes out to the browser. The real meat of the view is what sits in the middle, and that's the part that's fun to write. Described in Chapter 2, decorators are a great way to wrap several functions in some common code that can be written once and tested easily, which reduces both bugs and programmer fatigue. Since views are typically just standard Python functions, decorators can be used here as well.

Chapter 2 illustrated how decorators can be used to write a wrapper around the original function, which can then access all the arguments that were intended for that function, as well as the return value from the function itself. In terms of views, this means that decorators always have access to the incoming request object and the outgoing response object. In some cases, a decorator can be special-cased for a particular application, which would allow it to anticipate a greater number of arguments that are specific to that application.

There are a number of things decorators can offer views, and a few of them are common enough to warrant inclusion in Django itself. Living at django.views.decorators are a few packages containing decorators you can use on any view in any application. The following packages are listed with just the trailing portion of their full import path provided, given that they all live at the same location.

  • cache.cache_page—Stores the output of the view into the server's cache so that when similar requests come in later, the page doesn't have to be re-created each time.
  • cache.never_cache—Prevents caching for a particular view. This is useful if you have sitewide caching set up but certain views can't afford to go stale.
  • gzip.gzip_page—Compresses the output of the view and adds the appropriate HTTP headers so the Web browser knows how to handle it.
  • http.conditional_page—Only sends the whole page to the browser if it has changed since the last time the browser got a copy of it.
  • http.require_http_methods—Accepts a list of HTTP methods (described in detail in Chapter 7) that the view is limited to. If the view is called with any other method, it sends a response telling the browser it's not allowed, without even calling the view. Two included shortcut variations are http.require_GET and http.require_POST, which don't take any arguments and are hard-coded for GET and POST requests, respectively.
  • vary.vary_on_header—Helps control browser-based caching of pages by indicating that the page's content changes, depending on the values of the headers passed into the decorator. A simple variant specific to the Cookie header is available at vary.vary_on_cookie.

Additional decorators are provided as part of the bundled applications living at django. contrib. These decorators all live below that path, so as in the previous list, only the relevant path is supplied:

  • admin.views.decorators.staff_member_required—A simple decorator that checks the current user to see if it has staff access. This is used automatically for all the views in Django's built-in admin, but could also be used for any other staff-only views on your site. If the user doesn't have staff permissions, the decorator redirects the browser to the admin's login page.
  • auth.decorators.user_passes_test—Accepts a single argument, which is a function to test the current user against some arbitrary condition. The provided function should accept just the User object and return True if the test passes or False if it fails. If the test passes, the user will be granted access to the page, but if it fails, the browser will redirect to the site's login page, as determined by the LOGIN_URL setting.
  • auth.decorators.login_required—A specialized version of user_passes_test, this decorator simply checks that the user is logged in before allowing access to the view.
  • auth.decorators.permission_required—Another specialization of user_passes_test, this checks that the user has a given permission before the view is loaded. The decorator takes a single argument: the permission to be checked.

These are just the decorators that are bundled with Django itself. There are many other purposes for decorators, and third-party applications can provide their own as well. In order for these decorators to be of any use, however, they must be applied to views.

Applying View Decorators

Chapter 2 described how decorators can be applied to standard Python functions, both the newer syntax introduced in Python 2.4 and the older syntax, to retain compatibility with Python 2.3. Applying decorators to views works the same way, but there's a notable difference: views aren't always under your control.

The techniques described in Chapter 2 assume that the functions you decorate are your own. While that's often the case, the number of distributed applications means that many Django-powered Web sites will use code from other sources, with views of their own. Applying decorators as described previously would require changes to the third-party code.

The goal is to apply decorators to third-party views without actually modifying third-party code. The key to doing this lies in the older-style decorator syntax from Python 2.3 and earlier. Remember that the new syntax allows decorators to be applied above the function definition, but the older syntax relies on passing the function to the decorator directly. Since Python functions can be imported from anywhere and can be passed in as arguments at any time, this is an excellent way to create decorated views from third-party code.

Also remember that the URL configuration is defined in a Python module, which gets executed when it is read. This makes the wide array of Python available to this configuration, including the ability to pass functions into decorators to create new functions.

from django.conf.urls.defaults import *
from django.contrib.auth.decorators import login_required
from thirdpartyapp.views import special_view

urlpatterns = patterns('',
    (r'^private/special/$', login_required(special_view)),
)


Writing a View Decorator

Chapter 2 covered how decorators themselves work and how they can be written to work in a variety of situations, though decorators for views have a few specific details that should be noted. These have less to do with the technical side of writing decorators and more with the nuances of how to achieve certain useful effects when working with views specifically.

The most common task decorators are used for with views is to create a wrapper function around the original view. This allows the decorator to perform extra work beyond what the view itself would ordinarily do, including

  • Performing additional work based on the incoming request or altering its attributes
  • Altering the arguments to be passed to the view
  • Modifying or replacing the outgoing response
  • Handling errors that occur inside the view
  • Branching to some other code, without even executing the view

The first thing to consider when writing a decorator is that it receives all the arguments intended for the view itself. Previous sections covered this, but only in the usual context of using *args and **kwargs to receive the arguments and pass them straight through to the wrapped function. With views, you know in advance that the first argument will always be the incoming request object, so a wrapper function can anticipate this and receive the request separately from the other arguments.

By interacting with the request object prior to executing the view, decorators can do two important things: make decisions based on the incoming request and make changes to the request to alter how the view operates. These tasks aren't mutually exclusive and many decorators do both, such as the following example from Django.

from django.utils.functional import wraps

def set_test_cookie(view):
    """
    Automatically sets the test cookie on all anonymous users,
    so that they can be logged in more easily, without having
    to hit a separate login page.
    """
    def wrapper(request, *args, **kwargs):
        if request.user.is_anonymous():
            request.session.set_test_cookie()
        return view(request, *args, **kwargs)
    return wraps(view)(wrapper)


Another common use of decorators is to extract some common code from the beginning or end of a set of views. This can be especially useful when looking at incoming arguments, as decorators can perform any lookups and initializations prior to calling the view. Then, decorators can simply pass fully prepared objects to the view, rather than raw strings captured from a URL.

from django.utils.functional import wraps
from django.shortcuts import get_object_or_404

from news.models import Article

def get_article_from_id(view):
    """
    Retrieves a specific article, passing it to the view directly
    """

    def wrapper(request, id, *args, **kwargs):
        article = get_object_or_404(Article, id=int(id))
        return view(request, article=article, *args, **kwargs)
    return wraps(view)(wrapper)

The great thing about a decorator like this is that, even though the logic it contains is fairly minimal, it does cut down on the amount of code that has to be duplicated for views that all get an Article object according to an ID provided in the URL. This not only makes the views themselves a bit more readable, but any time you can cut down on code that has to be written, you can help reduce bugs.

Also, by having access to the response, decorators can make some interesting decisions about how that response should behave. Middleware classes, described in Chapter 7, have much more use for accessing the response, but there are still useful things decorators can do.

Of note is the ability to set the content-type of the response, which can control how the browser deals with the content once it receives it. Chapter 7 describes this in more detail and also how it can be set when creating the response. However, it's also possible to set it after the response has already been created and returned from a view.

This technique can be a good way to override the content-type for specific types of views. After all, if no content-type is specified, Django pulls a value from the DEFAULT_CONTENT_TYPE setting, which defaults to 'text/html'. For certain types of views, especially those intended for Web services, it may be better to serve them using another content_type, such as 'application/xml', while still being able to use generic views.

from django.utils.functional import wraps

def content_type(c_type):
    """
    Overrides the Content-Type provided by the view.
    Accepts a single argument, the new Content-Type
    value to be written to the outgoing response.
    """

    def decorator(view):
        def wrapper(request, *args, **kwargs):
            response = view(request, *args, **kwargs)
            response['Content_Type'] = c_type
        return wraps(view)(wrapper)
    return decorator

A lesser-used feature of view decorators is the ability to catch any exceptions that are raised by the view or any code it executes. Views typically just return a response directly, but there are still many situations where a view may opt to raise an exception instead. One common example, found in many of Django's own generic views, is raising the Http404 exception to indicate that an object couldn't be found.

Chapter 9 covers the exceptions Django provides in its standard distribution, many of which can be raised by views for one reason or another. In addition, many of the standard Python exceptions could be raised for various situations, and it can be useful to catch any of these. A decorator can perform a variety of additional tasks when an exception is raised, from simply logging the exception to the database to returning a different type of response in the case of certain exceptions.

Consider a custom logging application with a log entry model like this:

from datetime import datetime

from django.db impot models

class Entry(models.Model):
    path = models.CharField(max_length=255)
    type = models.CharField(max_length=255, db_index=True)
    date = models.DateTimeField(default=datetime.now, db_index=True)
    description = models.TextField()

The application providing this model could also provide a decorator for projects to apply to their own views that logs exceptions to this model automatically.

from django.utils.functional import wraps

from mylogapp.models import Entry

def logged(view):
    """
    Logs any errors that occurred during the view
    in a special model design for app_specific errors
    """

    def wrapper(request, *args, **kwargs):
        try:
            return view(request, *args, **kwargs)
        except Exception, e:
            #Log the entry using the application's Entry model
            Entry.objects.create(path=request.path, 'View exception', str(e))

            #Re-raise it so standard error handling still applies
            raise
    return wraps(view)(wrapper)

The recurring theme with all these examples is that view decorators can encapsulate some common code that would otherwise have to be duplicated in every instance of the view. In essence, view decorators are a way to extend the view's code before or after the original code. It's important to generalize these examples in order to realize just how much is possible with view decorators. Any boilerplate you find yourself duplicating at the beginning or end of your views is fair game to be placed in a decorator to save some time, energy and trouble.

Using an Object As a View

Remember, Django views don't always have to be standard functions; they just have to be callable. As described in Chapter 2, Python provides a way to define a class in such a way that instances of it can be called as if they were functions. If defined on a class, the __call__ method will be called when the object is passed in where a function is expected.

There are as many ways to use objects as views as there are ways to define objects themselves. Aside from using __call__ to receive each incoming request, what happens inside the object is up for grabs. In a typical situation, the request would be dispatched to individual methods based on certain criteria. This could be some aspect of the URL, the method of the request or even some parameters provided by the user.

import re

from django.views.generic import list_detail

class ObjectView(object):
    """
    Takes a model and provides a couple views designed to work with it.
    """
    regex = re.compile(r'(d+)/$')
    list = staticmethod(list_detail.object_list)
    detail = staticmethod(list_detail.object_detail)

    def __init__(self, model):
        self.queryset = model._default_manager.all()

    def __call__(self, request, url):
        match = self.regex.match(url)
        if match:
            return self.detail(request, queryset=self.queryset,  
                               object_id=int(match.group(1)))
        else:
            return self.list(request, queryset=self.queryset)

Because of the way classes must be declared for their instances to be considered callable, using an object as a view does require a bit more code than simple functions. Object-based views are certainly not a general-purpose solution to solve common problems, but they provide a few key advantages over traditional functions.

  • Higher degree of configurability
  • Easier customization for specialized applications
  • Reuse of objects that may be used for other purposes

With typical functions, views can be configured in only two ways: adding settings or providing arguments in a URL configuration. Settings are projectwide, which makes them unsuitable for configuring views, which may need different options for different purposes.

URL configurations provide the necessary hook to specify options, but typically each URL must be configured on its own, with little chance of reuse. The only alternative is to include a separate module, where individual views can be declared and all share the same configuration options specified when the module was included. This does work, but it requires that every single view in the included module know how to accept all the arguments that are provided.

In the previous example, the object allows a model's ID to be optional, displaying the model's detail if the ID is provided or a list of models otherwise. The view object accepts a model class that will automatically be passed into the standard generic views to provide this functionality. It would be used in a URL configuration as follows:

from django.conf.urls.defaults import *

from library.views import ObjectView
from library.models import Article

urlpatterns = patterns('',
    url(r'article/(.*)$', ObjectView(Article), name='article_view'),
)

Note how this URL pattern captures the entire end of the URL after the prefix. This is necessary to pass that content to the view, since Django doesn't realize there's more than just a single function at work here.

One of the biggest advantages of this approach is how easy it is to customize this object-based view in other code. If this was provided by a third-party application, for instance, an individual project may need to alter what views are called for the individual list and detail situations. By providing the view as a class, the site can create a subclass and simply override those methods that need altering.

Applied Techniques

By allowing custom objects and decorators to be used with URL patterns and views, nearly any valid Python code can customize how a URL is mapped to a view and how the view itself is executed. The following is just a taste of what's possible; the rest depends on the needs of your application.

Dual-Format Decorator

In this modern "Web 2.0" world, Web applications are often expected to make use of advanced techniques, such as XmlHttpRequest, to communicate with the server in various, interesting ways. Django supports this, but there are no "standard" mechanisms provided. Instead, each application must decide how best to handle these special types of interactions.

These "Web 2.0" applications often need to render the same content for standard browser requests as for these special types of requests, just in different formats, in order to make sure the application works without JavaScript enabled. It also helps to maintain individual URLs that can be bookmarked and retrieved as a whole, while still allowing just the essential content to be pulled up when necessary.

Since views normally return their responses directly, complete with full contents, this type of functionality only seems possible in one of two ways, each requiring a good deal of code duplication:

  • Write a separate view for each type of output, while sharing the internals with some third function that each view can call.
  • Write an if block inside the view that decides how its content should be output.

Ideally, views for this type of situation could be written without caring which output mechanism is expected. Because decorators are a common technique for removing boilerplate and they also have the ability to create or modify outgoing responses, they're perfectly suited for the task at hand. It's quite easy to write a decorator that automatically handles the decision of which path to take and creates an appropriate response based on data provided by the view.

The first step is to decide how a view should provide details of the response without creating the response directly. Since most views use templates to render their content and templates can use dictionaries to provide dynamic values within that content, a dictionary seems like a logical choice. Given that Django also comes bundled with a tool for converting simple Python objects—like dictionaries—directly to JavaScript objects, dictionaries become even more attractive as a way to pass data out of a view in a response_independent manner.

The next step is to determine what should be done with a dictionary returned by the view. Each type of response will have its own way of handling the dictionary, and the decorator should be written to handle as much as possible. Consider the normal case, where the dictionary will be passed to a template, which is then returned as the response. The decorator must perform these tasks:

  1. Retrieve the appropriate template.
  2. Call the original view, capturing the return value for later use.
  3. Create a RequestContext object with values returned by the view.
  4. Render the template using this context.
  5. Return an HttpResponse containing the rendered content.

That's a good bit of work to be done, but it's quite simple to manage inside a decorator. Another key to consider here is the fact that the decorator has to retrieve and render the template, which means it has to know what template to use. Rather than try to hard-code the template name in the decorator, it's best to let it take a single argument to pass in the template name when the decorator is applied. Here's what the code might look like before taking into account the ability to spit out two different types of responses.

from django.utils.functional import wraps
from django.template.loader import get_template
from django.template.context import RequestContext

def dual_format(template_name):
    def decorator(view):
        def wrapper(request, *args, **kwargs):
            data = view(request, *args, **kwargs)
            template = get_template(template_name)
            context = RequestContext(request, data)
            return template.render(context)
        return wraps(view)(wrapper)
    return decorator

This basic decorator will allow a view to have a template loaded, rendered and returned automatically. Simply provide a template name to the decorator and return a dictionary, and the decorator does the rest. Moving on to the other side of the problem, the decorator needs to be able to return a string that can be read by JavaScript, containing the same content that would otherwise be provided by the template.

Django comes built-in with a copy of simplejson,2 a library designed for converting Python objects into JavaScript Object Notation (JSON),3 so it can be easily consumed by code inside the Web browser. It even has a custom encoder for use with simplejson, designed to encode some of the particular types Django uses. This encoder can be used to transmit the dictionary's content directly to the browser, bypassing the template entirely. Combining it with request.is_ajax()—covered in Chapter 7—allows the decorator to decide which path to take, completing the equation.

from django.core.serializers.json import DjangoJSONEncoder
from django.utils.functional import wraps
from django.template.loader import get_template
from django.template.context import RequestContext

def dual_format(template_name):
    def decorator(view):
        def wrapper(request, *args, **kwargs):
            data = view(request, *args, **kwargs)
            if request.is_ajax():
            json = simplejson.dumps(data, cls=DjangoJSONEncoder)
            return HttpResponse(json)
        else:
            context = RequestContext(request)
            return render_to_response(template_name, data, context)
    return wraps(view)(wrapper)
return decorator

__________

Now What?

URLs form the foundation of your site's architecture, defining how users access the content and services you provide. Django stays out of the way when it comes to designing your URL scheme, so you're free to build it however you like. Be sure to take the appropriate time and remember that URL configuration is still a form of site design.

Views are the real workhorses of any application, taking user input and turning it into useful output. While the whole of Python is available for views to use, Django does provide one very important tool to handle one of the most common user input tasks on the Web: forms.

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

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